/* * * PPP library with GLib integration * * Copyright (C) 2009-2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "gatppp.h" #include "ppp.h" struct chap_header { guint8 code; guint8 identifier; guint16 length; guint8 data[0]; } __attribute__((packed)); struct ppp_chap { guint8 method; GAtPPP *ppp; }; enum chap_code { CHALLENGE = 1, RESPONSE, SUCCESS, FAILURE }; struct pap_header { guint8 code; guint8 identifier; guint16 length; guint8 data[0]; } __attribute__((packed)); struct ppp_pap { GAtPPP *ppp; struct ppp_header *authreq; guint16 authreq_len; guint retry_timer; guint retries; }; enum pap_code { PAP_REQUEST = 1, PAP_ACK, PAP_NAK }; /* * RFC 1334 2.1.1: * The Authenticate-Request packet MUST be repeated until a valid * reply packet is received, or an optional retry counter expires. * * If we don't get a reply after this many attempts, we can safely * assume we're never going to get one. */ #define PAP_MAX_RETRY 3 /* attempts */ #define PAP_TIMEOUT 10 /* seconds */ static void chap_process_challenge(struct ppp_chap *chap, const guint8 *packet) { const struct chap_header *header = (const struct chap_header *) packet; struct chap_header *response; GChecksum *checksum; const char *secret = g_at_ppp_get_password(chap->ppp); const char *username = g_at_ppp_get_username(chap->ppp); guint16 response_length; struct ppp_header *ppp_packet; gsize digest_len; /* create a checksum over id, secret, and challenge */ checksum = g_checksum_new(chap->method); if (checksum == NULL) return; g_checksum_update(checksum, &header->identifier, 1); if (secret) g_checksum_update(checksum, (guchar *) secret, strlen(secret)); g_checksum_update(checksum, &header->data[1], header->data[0]); /* transmit a response packet */ /* * allocate space for the header, the checksum, and the ppp header, * and the value size byte */ digest_len = g_checksum_type_get_length(chap->method); response_length = digest_len + sizeof(*header) + 1; if (username != NULL) response_length += strlen(username); ppp_packet = ppp_packet_new(response_length, CHAP_PROTOCOL); if (ppp_packet == NULL) goto challenge_out; response = (struct chap_header *) &ppp_packet->info; if (response) { response->code = RESPONSE; response->identifier = header->identifier; response->length = htons(response_length); g_checksum_get_digest(checksum, response->data + 1, &digest_len); response->data[0] = digest_len; /* leave the name empty? */ } if (username != NULL) memcpy(response->data + digest_len + 1, username, strlen(username)); /* transmit the packet */ ppp_transmit(chap->ppp, (guint8 *) ppp_packet, response_length); g_free(ppp_packet); challenge_out: g_checksum_free(checksum); } /* * parse the packet */ void ppp_chap_process_packet(struct ppp_chap *chap, const guint8 *new_packet, gsize len) { guint8 code; if (len < sizeof(struct chap_header)) return; code = new_packet[0]; switch (code) { case CHALLENGE: chap_process_challenge(chap, new_packet); break; case RESPONSE: break; case SUCCESS: ppp_auth_notify(chap->ppp, TRUE); break; case FAILURE: ppp_auth_notify(chap->ppp, FALSE); break; default: break; } } void ppp_chap_free(struct ppp_chap *chap) { g_free(chap); } struct ppp_chap *ppp_chap_new(GAtPPP *ppp, guint8 method) { struct ppp_chap *chap; if (method != MD5) return NULL; chap = g_try_new0(struct ppp_chap, 1); if (chap == NULL) return NULL; chap->ppp = ppp; chap->method = G_CHECKSUM_MD5; return chap; } void ppp_pap_process_packet(struct ppp_pap *pap, const guint8 *new_packet, gsize len) { guint8 code; if (len < sizeof(struct pap_header)) return; code = new_packet[0]; switch (code) { case PAP_ACK: g_source_remove(pap->retry_timer); pap->retry_timer = 0; ppp_auth_notify(pap->ppp, TRUE); break; case PAP_NAK: g_source_remove(pap->retry_timer); pap->retry_timer = 0; ppp_auth_notify(pap->ppp, FALSE); break; default: break; } } static gboolean ppp_pap_timeout(gpointer user_data) { struct ppp_pap *pap = (struct ppp_pap *)user_data; struct pap_header *authreq; if (++pap->retries >= PAP_MAX_RETRY) { pap->retry_timer = 0; ppp_auth_notify(pap->ppp, FALSE); return FALSE; } /* * RFC 1334 2.2.1: * The Identifier field MUST be changed each time an * Authenticate-Request packet is issued. */ authreq = (struct pap_header *)&pap->authreq->info; authreq->identifier++; ppp_transmit(pap->ppp, (guint8 *)pap->authreq, pap->authreq_len); return TRUE; } gboolean ppp_pap_start(struct ppp_pap *pap) { struct pap_header *authreq; struct ppp_header *packet; const char *username = g_at_ppp_get_username(pap->ppp); const char *password = g_at_ppp_get_password(pap->ppp); guint16 length; length = sizeof(*authreq) + strlen(username) + strlen(password) + 2; packet = ppp_packet_new(length, PAP_PROTOCOL); if (packet == NULL) return FALSE; pap->authreq = packet; pap->authreq_len = length; authreq = (struct pap_header *)&packet->info; authreq->code = PAP_REQUEST; authreq->identifier = 1; authreq->length = htons(length); authreq->data[0] = (unsigned char) strlen(username); memcpy(authreq->data + 1, username, strlen(username)); authreq->data[strlen(username) + 1] = (unsigned char)strlen(password); memcpy(authreq->data + 1 + strlen(username) + 1, password, strlen(password)); /* Transmit the packet and schedule a retry. */ ppp_transmit(pap->ppp, (guint8 *)packet, length); pap->retries = 0; pap->retry_timer = g_timeout_add_seconds(PAP_TIMEOUT, ppp_pap_timeout, pap); return TRUE; } void ppp_pap_free(struct ppp_pap *pap) { if (pap->retry_timer != 0) g_source_remove(pap->retry_timer); if (pap->authreq != NULL) g_free(pap->authreq); g_free(pap); } struct ppp_pap *ppp_pap_new(GAtPPP *ppp) { struct ppp_pap *pap; pap = g_try_new0(struct ppp_pap, 1); if (pap == NULL) return NULL; pap->ppp = ppp; return pap; }