struct pppcp_data *ipcp;
struct ppp_net *net;
struct ppp_chap *chap;
+ struct ppp_pap *pap;
GAtHDLC *hdlc;
gint mru;
gint mtu;
char username[256];
char password[256];
+ GAtPPPAuthMethod auth_method;
GAtPPPConnectFunc connect_cb;
gpointer connect_data;
GAtPPPDisconnectFunc disconnect_cb;
return TRUE;
break;
case PPP_PHASE_AUTHENTICATION:
- if (protocol != LCP_PROTOCOL && protocol != CHAP_PROTOCOL)
+ if (protocol != LCP_PROTOCOL && protocol != CHAP_PROTOCOL &&
+ protocol != PAP_PROTOCOL)
return TRUE;
break;
case PPP_PHASE_DEAD:
return TRUE;
case PPP_PHASE_NETWORK:
if (protocol != LCP_PROTOCOL && protocol != CHAP_PROTOCOL &&
+ protocol != PAP_PROTOCOL &&
protocol != IPCP_PROTO)
return TRUE;
break;
case IPCP_PROTO:
pppcp_process_packet(ppp->ipcp, packet, len - offset);
break;
+ case PAP_PROTOCOL:
+ if (ppp->pap)
+ ppp_pap_process_packet(ppp->pap, packet, len - offset);
+ else
+ pppcp_send_protocol_reject(ppp->lcp, buf, len);
+ break;
case CHAP_PROTOCOL:
if (ppp->chap) {
ppp_chap_process_packet(ppp->chap, packet,
guint16 proto = get_host_short(auth_data);
switch (proto) {
+ case PAP_PROTOCOL:
+ if (ppp->pap)
+ ppp_pap_free(ppp->pap);
+
+ ppp->pap = ppp_pap_new(ppp);
+ break;
case CHAP_PROTOCOL:
if (ppp->chap)
ppp_chap_free(ppp->chap);
void ppp_lcp_up_notify(GAtPPP *ppp)
{
- /* Wait for the peer to send us a challenge if we expect auth */
if (ppp->chap != NULL) {
+ /* Wait for the peer to send us a challenge. */
ppp_enter_phase(ppp, PPP_PHASE_AUTHENTICATION);
return;
+ } else if (ppp->pap != NULL) {
+ /* Try to send an Authenticate-Request and wait for reply. */
+ if (ppp_pap_start(ppp->pap) == TRUE)
+ ppp_enter_phase(ppp, PPP_PHASE_AUTHENTICATION);
+ else
+ /* It'll never work out. */
+ ppp_auth_notify(ppp, FALSE);
+ return;
}
/* Otherwise proceed as if auth succeeded */
return ppp->password;
}
+gboolean g_at_ppp_set_auth_method(GAtPPP *ppp, GAtPPPAuthMethod method)
+{
+ if (method != G_AT_PPP_AUTH_METHOD_CHAP &&
+ method != G_AT_PPP_AUTH_METHOD_PAP)
+ return FALSE;
+
+ ppp->auth_method = method;
+
+ return TRUE;
+}
+
+GAtPPPAuthMethod g_at_ppp_get_auth_method(GAtPPP *ppp)
+{
+ return ppp->auth_method;
+}
+
void g_at_ppp_set_recording(GAtPPP *ppp, const char *filename)
{
if (ppp == NULL)
else if (ppp->fd >= 0)
close(ppp->fd);
+ if (ppp->pap)
+ ppp_pap_free(ppp->pap);
+
if (ppp->chap)
ppp_chap_free(ppp->chap);
/* initialize IPCP state */
ppp->ipcp = ipcp_new(ppp, is_server, ip);
+ /* chap authentication by default */
+ ppp->auth_method = G_AT_PPP_AUTH_METHOD_CHAP;
+
return ppp;
}
G_AT_PPP_REASON_LOCAL_CLOSE, /* Normal user close */
} GAtPPPDisconnectReason;
+typedef enum _GAtPPPAuthMethod {
+ G_AT_PPP_AUTH_METHOD_CHAP,
+ G_AT_PPP_AUTH_METHOD_PAP,
+} GAtPPPAuthMethod;
+
typedef void (*GAtPPPConnectFunc)(const char *iface, const char *local,
const char *peer,
const char *dns1, const char *dns2,
const char *g_at_ppp_get_username(GAtPPP *ppp);
const char *g_at_ppp_get_password(GAtPPP *ppp);
+gboolean g_at_ppp_set_auth_method(GAtPPP *ppp, GAtPPPAuthMethod method);
+GAtPPPAuthMethod g_at_ppp_get_auth_method(GAtPPP *ppp);
+
void g_at_ppp_set_recording(GAtPPP *ppp, const char *filename);
void g_at_ppp_set_server_info(GAtPPP *ppp, const char *remote_ip,
#include "ppp_cp.h"
#define LCP_PROTOCOL 0xc021
+#define PAP_PROTOCOL 0xc023
#define CHAP_PROTOCOL 0xc223
#define IPCP_PROTO 0x8021
#define IPV6CP_PROTO 0x8057
struct ppp_chap;
struct ppp_net;
+struct ppp_pap;
struct ppp_header {
guint8 address;
void ppp_chap_process_packet(struct ppp_chap *chap, const guint8 *new_packet,
gsize len);
+/* PAP related functions */
+struct ppp_pap *ppp_pap_new(GAtPPP *ppp);
+void ppp_pap_free(struct ppp_pap *pap);
+gboolean ppp_pap_start(struct ppp_pap *pap);
+void ppp_pap_process_packet(struct ppp_pap *pap, const guint8 *new_packet,
+ gsize len);
+
/* TUN / Network related functions */
struct ppp_net *ppp_net_new(GAtPPP *ppp, int fd);
const char *ppp_net_get_interface(struct ppp_net *net);
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;
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;
+}
guint8 method = option_data[2];
guint8 *option;
- if ((proto == CHAP_PROTOCOL) && (method == MD5))
- break;
-
- /*
- * try to suggest CHAP & MD5. If we are out
- * of memory, just reject.
- */
-
- option = g_try_malloc0(5);
- if (option == NULL)
- return RCR_REJECT;
-
- option[0] = AUTH_PROTO;
- option[1] = 5;
- put_network_short(&option[2], CHAP_PROTOCOL);
- option[4] = MD5;
- *new_options = option;
- *new_len = 5;
- return RCR_NAK;
+ switch (g_at_ppp_get_auth_method(ppp)) {
+ case G_AT_PPP_AUTH_METHOD_CHAP:
+ if (proto == CHAP_PROTOCOL && method == MD5)
+ break;
+
+ /*
+ * Try to suggest CHAP/MD5.
+ * Just reject if we run out of memory.
+ */
+ option = g_try_malloc0(5);
+ if (option == NULL)
+ return RCR_REJECT;
+
+ option[0] = AUTH_PROTO;
+ option[1] = 5;
+ put_network_short(&option[2], CHAP_PROTOCOL);
+ option[4] = MD5;
+ *new_options = option;
+ *new_len = 5;
+
+ return RCR_NAK;
+
+ case G_AT_PPP_AUTH_METHOD_PAP:
+ if (proto == PAP_PROTOCOL)
+ break;
+
+ /*
+ * Try to suggest PAP.
+ * Just reject if we run out of memory.
+ */
+ option = g_try_malloc0(4);
+ if (option == NULL)
+ return RCR_REJECT;
+
+ option[0] = AUTH_PROTO;
+ option[1] = 4;
+ put_network_short(&option[2], PAP_PROTOCOL);
+ *new_options = option;
+ *new_len = 4;
+
+ return RCR_NAK;
+ }
+ break;
}
case ACCM:
case PFC: