ofono/gatchat/ppp_auth.c

312 lines
6.7 KiB
C

/*
*
* 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 <config.h>
#endif
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <arpa/inet.h>
#include <glib.h>
#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;
}