blob: 0f0a04a1c1e2776f2386189bc8ee0606aa12fee0 [file] [log] [blame]
/*
*
* Connection Manager
*
* Copyright (C) 2007-2010 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
*
*/
/* This file is built into a shared object which is loaded dynamically
* as a plugin into pppd.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
#include <fcntl.h>
#include <pppd/pppd.h>
#include <pppd/fsm.h>
#include <pppd/ipcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dbus/dbus.h>
#define INET_ADDRES_LEN (INET_ADDRSTRLEN + 5)
#define INET_DNS_LEN (2*INET_ADDRSTRLEN + 9)
static char *busname = NULL;
static char *interface = NULL;
static char *path = NULL;
static const char syslog_prefix[] = "libppp-plugin.so";
static DBusConnection *connection = NULL;
char pppd_version[] = VERSION;
int plugin_init(void);
static void append(DBusMessageIter *dict, const char *key, const char *value)
{
DBusMessageIter entry;
/* We clean the environment before invoking openconnect, but
might as well still filter out the few things that get
added that we're not interested in */
if (!strcmp(key, "PWD") || !strcmp(key, "_") ||
!strcmp(key, "SHLVL") || !strcmp(key, "connman_busname") ||
!strcmp(key, "connman_network"))
return;
dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
NULL, &entry);
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
dbus_message_iter_close_container(dict, &entry);
}
static const char *get_phase_name(int phase) {
static const char *names[] = {
"DEAD",
"INITIALIZE",
"SERIALCONN",
"DORMANT",
"ESTABLISH",
"AUTHENTICATE",
"CALLBACK",
"NETWORK",
"RUNNING",
"TERMINATE",
"DISCONNECT",
"HOLDOFF",
"MASTER"
};
if (phase < 0 || phase >= sizeof(names)/sizeof(names[0])) {
return "PHASE_UNKNOWN";
}
return names[phase];
}
static int pptp_have_secret()
{
return 1;
}
static int pptp_get_secret(char *username, char *password)
{
DBusMessage *msg, *reply;
const char *user, *pass;
DBusError err;
if (username == NULL && password == NULL) {
syslog(LOG_ERR, "%s: %s: username/password set to NULL",
syslog_prefix, __func__);
return -1;
}
if (password == NULL) {
syslog(LOG_ERR, "%s: %s: password set to NULL",
syslog_prefix, __func__);
return 1;
}
if (connection == NULL) {
syslog(LOG_ERR, "%s: %s: connection not set",
syslog_prefix, __func__);
return -1;
}
dbus_error_init(&err);
msg = dbus_message_new_method_call(busname, path,
interface, "getsec");
if (msg == NULL) {
syslog(LOG_ERR, "%s: %s: unable to create dbus call",
syslog_prefix, __func__);
return -1;
}
dbus_message_append_args(msg, DBUS_TYPE_INVALID, DBUS_TYPE_INVALID);
reply = dbus_connection_send_with_reply_and_block(connection,
msg, -1, &err);
if (reply == NULL) {
syslog(LOG_ERR, "%s: %s: unable to get dbus reply",
syslog_prefix, __func__);
if (dbus_error_is_set(&err) == TRUE)
dbus_error_free(&err);
dbus_message_unref(msg);
return -1;
}
dbus_message_unref(msg);
dbus_error_init(&err);
if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, &user,
DBUS_TYPE_STRING, &pass,
DBUS_TYPE_INVALID) == FALSE) {
syslog(LOG_ERR, "%s: %s: unable to get args",
syslog_prefix, __func__);
if (dbus_error_is_set(&err) == TRUE)
dbus_error_free(&err);
dbus_message_unref(reply);
return -1;
}
if (username != NULL)
strcpy(username, user);
strcpy(password, pass);
dbus_message_unref(reply);
return 1;
}
static void ppptp_up(void *data, int arg)
{
char buf[INET_ADDRES_LEN];
const char *reason = "connect";
DBusMessageIter iter, dict;
DBusMessage *msg;
char *resolved_server_address;
syslog(LOG_INFO, "%s: %s: interface up %s", syslog_prefix,
__func__, ifname);
if (connection == NULL) {
syslog(LOG_ERR, "%s: %s: connection not set",
syslog_prefix, __func__);
return;
}
if (ipcp_gotoptions[0].ouraddr == 0) {
syslog(LOG_ERR, "%s: %s: our address not set",
syslog_prefix, __func__);
return;
}
msg = dbus_message_new_method_call(busname, path,
interface, "notify");
if (msg == NULL) {
syslog(LOG_ERR, "%s: %s: unable to create dbus message",
syslog_prefix, __func__);
return;
}
dbus_message_set_no_reply(msg, TRUE);
dbus_message_append_args(msg,
DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID);
dbus_message_iter_init_append(msg, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
append(&dict, "INTERNAL_IFNAME", ifname);
inet_ntop(AF_INET, &ipcp_gotoptions[0].ouraddr, buf, INET_ADDRSTRLEN);
append(&dict, "INTERNAL_IP4_ADDRESS", buf);
inet_ntop(AF_INET, &ipcp_hisoptions[0].hisaddr, buf, INET_ADDRSTRLEN);
append(&dict, "EXTERNAL_IP4_ADDRESS", buf);
if (ipcp_gotoptions[0].default_route) {
inet_ntop(AF_INET, &ipcp_hisoptions[0].hisaddr, buf,
INET_ADDRSTRLEN);
append(&dict, "GATEWAY_ADDRESS", buf);
}
if (ipcp_gotoptions[0].dnsaddr[0] || ipcp_gotoptions[0].dnsaddr[1]) {
if (ipcp_gotoptions[0].dnsaddr[0]) {
inet_ntop(AF_INET, &ipcp_gotoptions[0].dnsaddr[0],
buf, INET_ADDRSTRLEN);
append(&dict, "DNS1", buf);
}
if (ipcp_gotoptions[0].dnsaddr[1]) {
inet_ntop(AF_INET, &ipcp_gotoptions[0].dnsaddr[1],
buf, INET_ADDRSTRLEN);
append(&dict, "DNS2", buf);
}
}
resolved_server_address = getenv("LNS_ADDRESS");
if (resolved_server_address != NULL)
append(&dict, "LNS_ADDRESS", resolved_server_address);
dbus_message_iter_close_container(&iter, &dict);
dbus_connection_send(connection, msg, NULL);
dbus_connection_flush(connection);
dbus_message_unref(msg);
}
static void pptp_exit(void *data, int arg)
{
if (connection != NULL) {
dbus_connection_unref(connection);
connection = NULL;
}
if (busname != NULL) {
free(busname);
busname = NULL;
}
if (interface != NULL) {
free(interface);
interface = NULL;
}
if (path != NULL) {
free(path);
path = NULL;
}
}
static void pptp_phase_change(void *data, int arg)
{
const char *reason = "disconnect";
DBusMessage *msg;
syslog(LOG_INFO, "%s: %s: Change to %s (%d)", syslog_prefix,
__func__, get_phase_name(arg), arg);
if (connection == NULL) {
syslog(LOG_ERR, "%s: %s: connection not set",
syslog_prefix, __func__);
return;
}
if (arg == PHASE_DEAD || arg == PHASE_DISCONNECT) {
msg = dbus_message_new_method_call(busname, path,
interface, "notify");
if (msg == NULL) {
syslog(LOG_ERR, "%s: %s: unable to create dbus "
"call", syslog_prefix, __func__);
return;
}
dbus_message_set_no_reply(msg, TRUE);
dbus_message_append_args(msg,
DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID);
dbus_connection_send(connection, msg, NULL);
dbus_connection_flush(connection);
dbus_message_unref(msg);
}
}
int plugin_init(void)
{
DBusError error;
static const char *bus, *inter, *p;
dbus_error_init(&error);
bus = getenv("CONNMAN_BUSNAME");
inter = getenv("CONNMAN_INTERFACE");
p = getenv("CONNMAN_PATH");
if (!bus || !inter || !p) {
syslog(LOG_ERR, "%s: %s: bus, interface, or path not set",
syslog_prefix, __func__);
return -1;
}
busname = strdup(bus);
interface = strdup(inter);
path = strdup(p);
if (!busname || !interface || !path) {
syslog(LOG_ERR, "%s: %s: alloc of bus, interface, or path "
"failed", syslog_prefix, __func__);
pptp_exit(NULL, 0);
return -1;
}
connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
if (connection == NULL) {
syslog(LOG_ERR, "%s: %s: connection not set",
syslog_prefix, __func__);
if (dbus_error_is_set(&error) == TRUE)
dbus_error_free(&error);
pptp_exit(NULL, 0);
return -1;
}
pap_passwd_hook = pptp_get_secret;
chap_passwd_hook = pptp_get_secret;
chap_check_hook = pptp_have_secret;
pap_check_hook = pptp_have_secret;
add_notifier(&ip_up_notifier, ppptp_up, NULL);
add_notifier(&phasechange, pptp_phase_change, NULL);
add_notifier(&exitnotify, pptp_exit, connection);
return 0;
}