2017-11-06 15:02:05 +03:00
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* nm-openvpn-service - openvpn integration with NetworkManager
*
* Copyright ( C ) 2005 - 2008 Tim Niemueller < tim @ niemueller . de >
* Copyright ( C ) 2005 - 2010 Dan Williams < dcbw @ redhat . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* $ Id : nm - openvpn - service . c 4232 2008 - 10 - 29 09 : 13 : 40 Z tambeti $
*
*/
# include "nm-default.h"
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>
# include <signal.h>
# include <sys/stat.h>
# include <sys/wait.h>
# include <sys/socket.h>
# include <sys/un.h>
# include <sys/types.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <ctype.h>
# include <errno.h>
# include <locale.h>
# include <pwd.h>
# include <grp.h>
# include <glib-unix.h>
# include "utils.h"
# include "nm-utils/nm-shared-utils.h"
# include "nm-utils/nm-vpn-plugin-macros.h"
# if !defined(DIST_VERSION)
# define DIST_VERSION VERSION
# endif
2017-11-06 15:08:27 +03:00
// TODO remove me -- just for getting rid of error underlining
# ifndef LOCALSTATEDIR
# define LOCALSTATEDIR ""
# endif
# ifndef LIBEXECDIR
# define LIBEXECDIR ""
# endif
# ifndef NM_OPENVPN_LOCALEDIR
# define NM_OPENVPN_LOCALEDIR ""
# endif
# ifndef NM_WIREGUARD_LOCALEDIR
# define NM_WIREGUARD_LOCALEDIR ""
# endif
2017-11-06 15:02:05 +03:00
# define RUNDIR LOCALSTATEDIR" / run / NetworkManager"
static struct {
gboolean debug ;
int log_level ;
int log_level_ovpn ;
bool log_syslog ;
GSList * pids_pending_list ;
} gl /*obal*/ ;
# define NM_OPENVPN_HELPER_PATH LIBEXECDIR" / nm-openvpn-service-openvpn-helper"
/*****************************************************************************/
2017-11-06 15:08:27 +03:00
# define NM_TYPE_WIREGUARD_PLUGIN (nm_wireguard_plugin_get_type ())
# define NM_WIREGUARD_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIREGUARD_PLUGIN, NMWireguardPlugin))
# define NM_WIREGUARD_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_WIREGUARD_PLUGIN, NMWireguardPluginClass))
# define NM_IS_WIREGUARD_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_WIREGUARD_PLUGIN))
# define NM_IS_WIREGUARD_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_WIREGUARD_PLUGIN))
# define NM_WIREGUARD_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_WIREGUARD_PLUGIN, NMWireguardPluginClass))
2017-11-06 15:02:05 +03:00
typedef struct {
NMVpnServicePlugin parent ;
2017-11-06 15:08:27 +03:00
} NMWireguardPlugin ;
2017-11-06 15:02:05 +03:00
typedef struct {
NMVpnServicePluginClass parent ;
2017-11-06 15:08:27 +03:00
} NMWireguardPluginClass ;
2017-11-06 15:02:05 +03:00
2017-11-06 15:08:27 +03:00
GType nm_wireguard_plugin_get_type ( void ) ;
2017-11-06 15:02:05 +03:00
2017-11-06 15:08:27 +03:00
NMWireguardPlugin * nm_wireguard_plugin_new ( const char * bus_name ) ;
2017-11-06 15:02:05 +03:00
/*****************************************************************************/
typedef enum {
OPENVPN_BINARY_VERSION_INVALID ,
OPENVPN_BINARY_VERSION_UNKNOWN ,
OPENVPN_BINARY_VERSION_2_3_OR_OLDER ,
OPENVPN_BINARY_VERSION_2_4_OR_NEWER ,
} OpenvpnBinaryVersion ;
typedef struct {
GPid pid ;
guint watch_id ;
guint kill_id ;
2017-11-06 15:08:27 +03:00
NMWireguardPlugin * plugin ;
2017-11-06 15:02:05 +03:00
} PidsPendingData ;
typedef struct {
char * default_username ;
char * username ;
char * password ;
char * priv_key_pass ;
char * proxy_username ;
char * proxy_password ;
char * pending_auth ;
char * challenge_state_id ;
char * challenge_text ;
GIOChannel * socket_channel ;
guint socket_channel_eventid ;
2017-11-06 15:08:27 +03:00
} NMWireguardPluginIOData ;
2017-11-06 15:02:05 +03:00
typedef struct {
GPid pid ;
guint connect_timer ;
guint connect_count ;
2017-11-06 15:08:27 +03:00
NMWireguardPluginIOData * io_data ;
2017-11-06 15:02:05 +03:00
gboolean interactive ;
char * mgt_path ;
2017-11-06 15:08:27 +03:00
} NMWireguardPluginPrivate ;
2017-11-06 15:02:05 +03:00
2017-11-06 15:08:27 +03:00
G_DEFINE_TYPE ( NMWireguardPlugin , nm_wireguard_plugin , NM_TYPE_VPN_SERVICE_PLUGIN )
2017-11-06 15:02:05 +03:00
2017-11-06 15:08:27 +03:00
# define NM_WIREGUARD_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_WIREGUARD_PLUGIN, NMWireguardPluginPrivate))
2017-11-06 15:02:05 +03:00
/*****************************************************************************/
typedef struct {
const char * name ;
GType type ;
gint int_min ;
gint int_max ;
gboolean address ;
} ValidProperty ;
static const ValidProperty valid_properties [ ] = {
{ NM_OPENVPN_KEY_AUTH , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_CA , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_CERT , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_CIPHER , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_KEYSIZE , G_TYPE_INT , 1 , 65535 , FALSE } ,
{ NM_OPENVPN_KEY_COMP_LZO , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_CONNECTION_TYPE , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_FLOAT , G_TYPE_BOOLEAN , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_FRAGMENT_SIZE , G_TYPE_INT , 0 , G_MAXINT , FALSE } ,
{ NM_OPENVPN_KEY_KEY , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_LOCAL_IP , G_TYPE_STRING , 0 , 0 , TRUE } ,
{ NM_OPENVPN_KEY_MSSFIX , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_MTU_DISC , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_PING , G_TYPE_INT , 0 , G_MAXINT , FALSE } ,
{ NM_OPENVPN_KEY_PING_EXIT , G_TYPE_INT , 0 , G_MAXINT , FALSE } ,
{ NM_OPENVPN_KEY_PING_RESTART , G_TYPE_INT , 0 , G_MAXINT , FALSE } ,
{ NM_OPENVPN_KEY_MAX_ROUTES , G_TYPE_INT , 0 , 100000000 , FALSE } ,
{ NM_OPENVPN_KEY_PROTO_TCP , G_TYPE_BOOLEAN , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_PORT , G_TYPE_INT , 1 , 65535 , FALSE } ,
{ NM_OPENVPN_KEY_PROXY_TYPE , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_PROXY_SERVER , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_PROXY_PORT , G_TYPE_INT , 1 , 65535 , FALSE } ,
{ NM_OPENVPN_KEY_PROXY_RETRY , G_TYPE_BOOLEAN , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_HTTP_PROXY_USERNAME , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_REMOTE , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_REMOTE_RANDOM , G_TYPE_BOOLEAN , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_REMOTE_IP , G_TYPE_STRING , 0 , 0 , TRUE } ,
{ NM_OPENVPN_KEY_RENEG_SECONDS , G_TYPE_INT , 0 , G_MAXINT , FALSE } ,
{ NM_OPENVPN_KEY_STATIC_KEY , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_STATIC_KEY_DIRECTION , G_TYPE_INT , 0 , 1 , FALSE } ,
{ NM_OPENVPN_KEY_TA , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_TA_DIR , G_TYPE_INT , 0 , 1 , FALSE } ,
{ NM_OPENVPN_KEY_TAP_DEV , G_TYPE_BOOLEAN , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_DEV , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_DEV_TYPE , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_TUN_IPV6 , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_TLS_CIPHER , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_TLS_CRYPT , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_TLS_REMOTE , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_VERIFY_X509_NAME , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_REMOTE_CERT_TLS , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_NS_CERT_TYPE , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_TUNNEL_MTU , G_TYPE_INT , 0 , G_MAXINT , FALSE } ,
{ NM_OPENVPN_KEY_USERNAME , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_PASSWORD " -flags " , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_CERTPASS " -flags " , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_NOSECRET , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD " -flags " , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NULL , G_TYPE_NONE , FALSE }
} ;
static const ValidProperty valid_secrets [ ] = {
{ NM_OPENVPN_KEY_PASSWORD , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_CERTPASS , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_NOSECRET , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD , G_TYPE_STRING , 0 , 0 , FALSE } ,
{ NULL , G_TYPE_NONE , FALSE }
} ;
/*****************************************************************************/
# define _NMLOG(level, ...) \
G_STMT_START { \
if ( gl . log_level > = ( level ) ) { \
g_print ( " nm-openvpn[%ld] %-7s " _NM_UTILS_MACRO_FIRST ( __VA_ARGS__ ) " \n " , \
( long ) getpid ( ) , \
nm_utils_syslog_to_str ( level ) \
_NM_UTILS_MACRO_REST ( __VA_ARGS__ ) ) ; \
} \
} G_STMT_END
static gboolean
_LOGD_enabled ( void )
{
return gl . log_level > = LOG_INFO ;
}
# define _LOGD(...) _NMLOG(LOG_INFO, __VA_ARGS__)
# define _LOGI(...) _NMLOG(LOG_NOTICE, __VA_ARGS__)
# define _LOGW(...) _NMLOG(LOG_WARNING, __VA_ARGS__)
/*****************************************************************************/
2017-12-20 15:00:13 +03:00
static const char *
wg_quick_find_exepath ( void )
{
static const char * paths [ ] = {
" /usr/sbin/wg-quick " ,
" /usr/bin/wg-quick " ,
" /sbin/wg-quick " ,
" /bin/wg-quick " ,
" /usr/local/sbin/wg-quick " ,
" /usr/local/bin/wg-quick " ,
} ;
int i ;
for ( i = 0 ; i < G_N_ELEMENTS ( paths ) ; i + + ) {
if ( g_file_test ( paths [ i ] , G_FILE_TEST_EXISTS ) ) {
return paths [ i ] ;
}
}
return NULL ;
}
2017-11-06 15:02:05 +03:00
static const char *
openvpn_binary_find_exepath ( void )
{
static const char * paths [ ] = {
" /usr/sbin/openvpn " ,
" /sbin/openvpn " ,
" /usr/local/sbin/openvpn " ,
} ;
int i ;
for ( i = 0 ; i < G_N_ELEMENTS ( paths ) ; i + + ) {
if ( g_file_test ( paths [ i ] , G_FILE_TEST_EXISTS ) )
return paths [ i ] ;
}
return NULL ;
}
static OpenvpnBinaryVersion
openvpn_binary_detect_version ( const char * exepath )
{
gs_free char * s_stdout = NULL ;
const char * s ;
int exit_code ;
int n ;
g_return_val_if_fail ( exepath & & exepath [ 0 ] = = ' / ' , OPENVPN_BINARY_VERSION_UNKNOWN ) ;
if ( ! g_spawn_sync ( NULL ,
( char * [ ] ) { ( char * ) exepath , " --version " , NULL } ,
NULL ,
G_SPAWN_STDERR_TO_DEV_NULL ,
NULL ,
NULL ,
& s_stdout ,
NULL ,
& exit_code ,
NULL ) )
return OPENVPN_BINARY_VERSION_UNKNOWN ;
if ( ! WIFEXITED ( exit_code )
| | WEXITSTATUS ( exit_code ) ! = 1 ) {
/* expect return code 1 (OPENVPN_EXIT_STATUS_USAGE) */
return OPENVPN_BINARY_VERSION_UNKNOWN ;
}
/* the output for --version starts with title_string, which starts with PACKAGE_STRING,
* which looks like " OpenVPN 2.#... " . Do a strict parsing here . . . */
if ( ! s_stdout
| | ! g_str_has_prefix ( s_stdout , " OpenVPN 2. " ) )
return OPENVPN_BINARY_VERSION_UNKNOWN ;
s = & s_stdout [ NM_STRLEN ( " OpenVPN 2. " ) ] ;
if ( ! g_ascii_isdigit ( s [ 0 ] ) )
return OPENVPN_BINARY_VERSION_UNKNOWN ;
n = 0 ;
do {
if ( n > G_MAXINT / 100 )
return OPENVPN_BINARY_VERSION_UNKNOWN ;
n = ( n * 10 ) + ( s [ 0 ] - ' 0 ' ) ;
} while ( g_ascii_isdigit ( ( + + s ) [ 0 ] ) ) ;
if ( n < = 3 )
return OPENVPN_BINARY_VERSION_2_3_OR_OLDER ;
return OPENVPN_BINARY_VERSION_2_4_OR_NEWER ;
}
static OpenvpnBinaryVersion
openvpn_binary_detect_version_cached ( const char * exepath , OpenvpnBinaryVersion * cached )
{
if ( G_UNLIKELY ( * cached = = OPENVPN_BINARY_VERSION_INVALID ) )
* cached = openvpn_binary_detect_version ( exepath ) ;
return * cached ;
}
/*****************************************************************************/
static void
pids_pending_data_free ( PidsPendingData * pid_data )
{
nm_clear_g_source ( & pid_data - > watch_id ) ;
nm_clear_g_source ( & pid_data - > kill_id ) ;
if ( pid_data - > plugin )
g_object_remove_weak_pointer ( ( GObject * ) pid_data - > plugin , ( gpointer * ) & pid_data - > plugin ) ;
g_slice_free ( PidsPendingData , pid_data ) ;
}
static PidsPendingData *
pids_pending_get ( GPid pid )
{
GSList * iter ;
for ( iter = gl . pids_pending_list ; iter ; iter = iter - > next ) {
if ( ( ( PidsPendingData * ) iter - > data ) - > pid = = pid )
return iter - > data ;
}
g_return_val_if_reached ( NULL ) ;
}
2017-11-06 15:08:27 +03:00
static void openvpn_child_terminated ( NMWireguardPlugin * plugin , GPid pid , gint status ) ;
2017-11-06 15:02:05 +03:00
static void
pids_pending_child_watch_cb ( GPid pid , gint status , gpointer user_data )
{
PidsPendingData * pid_data = user_data ;
2017-11-06 15:08:27 +03:00
NMWireguardPlugin * plugin ;
2017-11-06 15:02:05 +03:00
if ( WIFEXITED ( status ) ) {
int exit_status ;
exit_status = WEXITSTATUS ( status ) ;
if ( exit_status ! = 0 )
_LOGW ( " openvpn[%ld] exited with error code %d " , ( long ) pid , exit_status ) ;
else
_LOGI ( " openvpn[%ld] exited with success " , ( long ) pid ) ;
}
else if ( WIFSTOPPED ( status ) )
_LOGW ( " openvpn[%ld] stopped unexpectedly with signal %d " , ( long ) pid , WSTOPSIG ( status ) ) ;
else if ( WIFSIGNALED ( status ) )
_LOGW ( " openvpn[%ld] died with signal %d " , ( long ) pid , WTERMSIG ( status ) ) ;
else
_LOGW ( " openvpn[%ld] died from an unnatural cause " , ( long ) pid ) ;
g_return_if_fail ( pid_data ) ;
g_return_if_fail ( pid_data - > pid = = pid ) ;
g_return_if_fail ( g_slist_find ( gl . pids_pending_list , pid_data ) ) ;
plugin = pid_data - > plugin ;
pid_data - > watch_id = 0 ;
gl . pids_pending_list = g_slist_remove ( gl . pids_pending_list , pid_data ) ;
pids_pending_data_free ( pid_data ) ;
if ( plugin )
openvpn_child_terminated ( plugin , pid , status ) ;
}
static void
2017-11-06 15:08:27 +03:00
pids_pending_add ( GPid pid , NMWireguardPlugin * plugin )
2017-11-06 15:02:05 +03:00
{
PidsPendingData * pid_data ;
2017-11-06 15:08:27 +03:00
g_return_if_fail ( NM_IS_WIREGUARD_PLUGIN ( plugin ) ) ;
2017-11-06 15:02:05 +03:00
g_return_if_fail ( pid > 0 ) ;
_LOGI ( " openvpn[%ld] started " , ( long ) pid ) ;
pid_data = g_slice_new ( PidsPendingData ) ;
pid_data - > pid = pid ;
pid_data - > kill_id = 0 ;
pid_data - > watch_id = g_child_watch_add ( pid , pids_pending_child_watch_cb , pid_data ) ;
pid_data - > plugin = plugin ;
g_object_add_weak_pointer ( ( GObject * ) plugin , ( gpointer * ) & pid_data - > plugin ) ;
gl . pids_pending_list = g_slist_prepend ( gl . pids_pending_list , pid_data ) ;
}
2017-12-20 15:00:13 +03:00
// TODO implement me right
wg_quick_closed ( GPid pid , gint status , gpointer user_data ) {
PidsPendingData * pid_data = user_data ;
NMWireguardPlugin * plugin ;
if ( WIFEXITED ( status ) ) {
int exit_status ;
exit_status = WEXITSTATUS ( status ) ;
if ( exit_status ! = 0 )
_LOGW ( " wg-quick[%ld] exited with error code %d " , ( long ) pid , exit_status ) ;
else
_LOGI ( " wg-quick[%ld] exited with success " , ( long ) pid ) ;
}
else if ( WIFSTOPPED ( status ) )
_LOGW ( " wg-quick[%ld] stopped unexpectedly with signal %d " , ( long ) pid , WSTOPSIG ( status ) ) ;
else if ( WIFSIGNALED ( status ) )
_LOGW ( " wg-quick[%ld] died with signal %d " , ( long ) pid , WTERMSIG ( status ) ) ;
else
_LOGW ( " wg-quick[%ld] died from an unnatural cause " , ( long ) pid ) ;
g_return_if_fail ( pid_data ) ;
g_return_if_fail ( pid_data - > pid = = pid ) ;
g_return_if_fail ( g_slist_find ( gl . pids_pending_list , pid_data ) ) ;
plugin = pid_data - > plugin ;
pid_data - > watch_id = 0 ;
gl . pids_pending_list = g_slist_remove ( gl . pids_pending_list , pid_data ) ;
pids_pending_data_free ( pid_data ) ;
}
// TODO implement me right
static void
pids_pending_add_wg ( GPid pid , NMWireguardPlugin * plugin )
{
PidsPendingData * pid_data ;
g_return_if_fail ( NM_IS_WIREGUARD_PLUGIN ( plugin ) ) ;
g_return_if_fail ( pid > 0 ) ;
_LOGI ( " wg-quick[%ld] started " , ( long ) pid ) ;
pid_data = g_slice_new ( PidsPendingData ) ;
pid_data - > pid = pid ;
pid_data - > kill_id = 0 ;
pid_data - > watch_id = g_child_watch_add ( pid , wg_quick_closed , pid_data ) ;
pid_data - > plugin = plugin ;
g_object_add_weak_pointer ( ( GObject * ) plugin , ( gpointer * ) & pid_data - > plugin ) ;
gl . pids_pending_list = g_slist_prepend ( gl . pids_pending_list , pid_data ) ;
}
2017-11-06 15:02:05 +03:00
static gboolean
pids_pending_ensure_killed ( gpointer user_data )
{
PidsPendingData * pid_data = user_data ;
g_return_val_if_fail ( pid_data & & pid_data = = pids_pending_get ( pid_data - > pid ) , FALSE ) ;
_LOGI ( " openvpn[%ld]: send SIGKILL " , ( long ) pid_data - > pid ) ;
pid_data - > kill_id = 0 ;
kill ( pid_data - > pid , SIGKILL ) ;
return FALSE ;
}
static void
pids_pending_send_sigterm ( GPid pid )
{
PidsPendingData * pid_data ;
pid_data = pids_pending_get ( pid ) ;
g_return_if_fail ( pid_data ) ;
_LOGI ( " openvpn[%ld]: send SIGTERM " , ( long ) pid ) ;
kill ( pid , SIGTERM ) ;
pid_data - > kill_id = g_timeout_add ( 2000 , pids_pending_ensure_killed , pid_data ) ;
}
static void
pids_pending_wait_for_processes ( GMainLoop * main_loop )
{
if ( gl . pids_pending_list ) {
_LOGI ( " wait for %u openvpn processes to terminate... " , g_slist_length ( gl . pids_pending_list ) ) ;
do {
g_main_context_iteration ( g_main_loop_get_context ( main_loop ) , TRUE ) ;
} while ( gl . pids_pending_list ) ;
}
}
/*****************************************************************************/
static gboolean
validate_address ( const char * address )
{
const char * p = address ;
if ( ! address | | ! strlen ( address ) )
return FALSE ;
/* Ensure it's a valid DNS name or IP address */
while ( * p ) {
if ( ! isalnum ( * p ) & & ( * p ! = ' - ' ) & & ( * p ! = ' . ' ) )
return FALSE ;
p + + ;
}
return TRUE ;
}
typedef struct ValidateInfo {
const ValidProperty * table ;
GError * * error ;
gboolean have_items ;
} ValidateInfo ;
static void
validate_one_property ( const char * key , const char * value , gpointer user_data )
{
ValidateInfo * info = ( ValidateInfo * ) user_data ;
int i ;
if ( * ( info - > error ) )
return ;
info - > have_items = TRUE ;
/* 'name' is the setting name; always allowed but unused */
if ( ! strcmp ( key , NM_SETTING_NAME ) )
return ;
for ( i = 0 ; info - > table [ i ] . name ; i + + ) {
const ValidProperty * prop = & info - > table [ i ] ;
long int tmp ;
if ( strcmp ( prop - > name , key ) )
continue ;
switch ( prop - > type ) {
case G_TYPE_STRING :
if ( ! prop - > address | | validate_address ( value ) )
return ; /* valid */
g_set_error ( info - > error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " invalid address “%s” " ) ,
key ) ;
break ;
case G_TYPE_INT :
errno = 0 ;
tmp = strtol ( value , NULL , 10 ) ;
if ( errno = = 0 & & tmp > = prop - > int_min & & tmp < = prop - > int_max )
return ; /* valid */
g_set_error ( info - > error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " invalid integer property “%s” or out of range [%d -> %d] " ) ,
key , prop - > int_min , prop - > int_max ) ;
break ;
case G_TYPE_BOOLEAN :
if ( ! strcmp ( value , " yes " ) | | ! strcmp ( value , " no " ) )
return ; /* valid */
g_set_error ( info - > error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
/* Translators: keep "yes" and "no" untranslated! */
_ ( " invalid boolean property “%s” (not yes or no) " ) ,
key ) ;
break ;
default :
g_set_error ( info - > error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " unhandled property “%s” type %s " ) ,
key , g_type_name ( prop - > type ) ) ;
break ;
}
}
/* Did not find the property from valid_properties or the type did not match */
if ( ! info - > table [ i ] . name ) {
g_set_error ( info - > error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " property “%s” invalid or not supported " ) ,
key ) ;
}
}
static gboolean
nm_openvpn_properties_validate ( NMSettingVpn * s_vpn , GError * * error )
{
GError * validate_error = NULL ;
ValidateInfo info = { & valid_properties [ 0 ] , & validate_error , FALSE } ;
nm_setting_vpn_foreach_data_item ( s_vpn , validate_one_property , & info ) ;
if ( ! info . have_items ) {
g_set_error_literal ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " No VPN configuration options. " ) ) ;
return FALSE ;
}
if ( validate_error ) {
* error = validate_error ;
return FALSE ;
}
return TRUE ;
}
static gboolean
nm_openvpn_secrets_validate ( NMSettingVpn * s_vpn , GError * * error )
{
GError * validate_error = NULL ;
ValidateInfo info = { & valid_secrets [ 0 ] , & validate_error , FALSE } ;
nm_setting_vpn_foreach_secret ( s_vpn , validate_one_property , & info ) ;
if ( validate_error ) {
g_propagate_error ( error , validate_error ) ;
return FALSE ;
}
return TRUE ;
}
static void
2017-11-06 15:08:27 +03:00
nm_openvpn_disconnect_management_socket ( NMWireguardPlugin * plugin )
2017-11-06 15:02:05 +03:00
{
2017-11-06 15:08:27 +03:00
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
NMWireguardPluginIOData * io_data = priv - > io_data ;
2017-11-06 15:02:05 +03:00
/* This should not throw a warning since this can happen in
non - password modes */
if ( ! io_data )
return ;
if ( io_data - > socket_channel_eventid )
g_source_remove ( io_data - > socket_channel_eventid ) ;
if ( io_data - > socket_channel ) {
g_io_channel_shutdown ( io_data - > socket_channel , FALSE , NULL ) ;
g_io_channel_unref ( io_data - > socket_channel ) ;
}
g_free ( io_data - > username ) ;
g_free ( io_data - > proxy_username ) ;
g_free ( io_data - > pending_auth ) ;
if ( io_data - > password )
memset ( io_data - > password , 0 , strlen ( io_data - > password ) ) ;
g_free ( io_data - > password ) ;
if ( io_data - > priv_key_pass )
memset ( io_data - > priv_key_pass , 0 , strlen ( io_data - > priv_key_pass ) ) ;
g_free ( io_data - > priv_key_pass ) ;
if ( io_data - > proxy_password )
memset ( io_data - > proxy_password , 0 , strlen ( io_data - > proxy_password ) ) ;
g_free ( io_data - > proxy_password ) ;
g_free ( io_data - > challenge_state_id ) ;
g_free ( io_data - > challenge_text ) ;
g_free ( priv - > io_data ) ;
priv - > io_data = NULL ;
}
static char *
ovpn_quote_string ( const char * unquoted )
{
char * quoted = NULL , * q ;
char * u = ( char * ) unquoted ;
g_return_val_if_fail ( unquoted ! = NULL , NULL ) ;
/* FIXME: use unpaged memory */
quoted = q = g_malloc0 ( strlen ( unquoted ) * 2 ) ;
while ( * u ) {
/* Escape certain characters */
if ( * u = = ' ' | | * u = = ' \\ ' | | * u = = ' " ' )
* q + + = ' \\ ' ;
* q + + = * u + + ;
}
return quoted ;
}
static char *
get_detail ( const char * input , const char * prefix )
{
const char * end ;
nm_assert ( prefix ) ;
if ( ! g_str_has_prefix ( input , prefix ) )
return NULL ;
/* Grab characters until the next ' */
input + = strlen ( prefix ) ;
end = strchr ( input , ' \' ' ) ;
if ( end )
return g_strndup ( input , end - input ) ;
return NULL ;
}
/* Parse challenge response protocol message of the form
* CRV1 : flags : state_id : username : text
*/
static gboolean
parse_challenge ( const char * failure_reason , char * * challenge_state_id , char * * challenge_text )
{
const char * colon [ 4 ] ;
if ( ! failure_reason
| | ! g_str_has_prefix ( failure_reason , " CRV1: " ) )
return FALSE ;
colon [ 0 ] = strchr ( failure_reason , ' : ' ) ;
if ( ! colon [ 0 ] )
return FALSE ;
colon [ 1 ] = strchr ( colon [ 0 ] + 1 , ' : ' ) ;
if ( ! colon [ 1 ] )
return FALSE ;
colon [ 2 ] = strchr ( colon [ 1 ] + 1 , ' : ' ) ;
if ( ! colon [ 2 ] )
return FALSE ;
colon [ 3 ] = strchr ( colon [ 2 ] + 1 , ' : ' ) ;
if ( ! colon [ 3 ] )
return FALSE ;
* challenge_state_id = g_strndup ( colon [ 1 ] + 1 , colon [ 2 ] - colon [ 1 ] - 1 ) ;
* challenge_text = g_strdup ( colon [ 3 ] + 1 ) ;
return TRUE ;
}
static void
write_user_pass ( GIOChannel * channel ,
const char * authtype ,
const char * user ,
const char * pass )
{
char * quser , * qpass , * buf ;
/* Quote strings passed back to openvpn */
quser = ovpn_quote_string ( user ) ;
qpass = ovpn_quote_string ( pass ) ;
buf = g_strdup_printf ( " username \" %s \" \" %s \" \n "
" password \" %s \" \" %s \" \n " ,
authtype , quser ,
authtype , qpass ) ;
memset ( qpass , 0 , strlen ( qpass ) ) ;
g_free ( qpass ) ;
g_free ( quser ) ;
/* Will always write everything in blocking channels (on success) */
g_io_channel_write_chars ( channel , buf , strlen ( buf ) , NULL , NULL ) ;
g_io_channel_flush ( channel , NULL ) ;
memset ( buf , 0 , strlen ( buf ) ) ;
g_free ( buf ) ;
}
static gboolean
2017-11-06 15:08:27 +03:00
handle_auth ( NMWireguardPluginIOData * io_data ,
2017-11-06 15:02:05 +03:00
const char * requested_auth ,
const char * * out_message ,
char * * * out_hints )
{
gboolean handled = FALSE ;
guint i = 0 ;
char * * hints = NULL ;
g_return_val_if_fail ( requested_auth ! = NULL , FALSE ) ;
g_return_val_if_fail ( out_message ! = NULL , FALSE ) ;
g_return_val_if_fail ( out_hints ! = NULL , FALSE ) ;
if ( strcmp ( requested_auth , " Auth " ) = = 0 ) {
const char * username = io_data - > username ;
/* Fall back to the default username if it wasn't overridden by the user */
if ( ! username )
username = io_data - > default_username ;
if ( username ! = NULL & & io_data - > password ! = NULL & & io_data - > challenge_state_id ) {
gs_free char * response = NULL ;
response = g_strdup_printf ( " CRV1::%s::%s " ,
io_data - > challenge_state_id ,
io_data - > password ) ;
write_user_pass ( io_data - > socket_channel ,
requested_auth ,
username ,
response ) ;
nm_clear_g_free ( & io_data - > challenge_state_id ) ;
nm_clear_g_free ( & io_data - > challenge_text ) ;
} else if ( username ! = NULL & & io_data - > password ! = NULL ) {
write_user_pass ( io_data - > socket_channel ,
requested_auth ,
username ,
io_data - > password ) ;
} else {
hints = g_new0 ( char * , 3 ) ;
if ( ! username ) {
hints [ i + + ] = NM_OPENVPN_KEY_USERNAME ;
* out_message = _ ( " A username is required. " ) ;
}
if ( ! io_data - > password ) {
hints [ i + + ] = NM_OPENVPN_KEY_PASSWORD ;
* out_message = _ ( " A password is required. " ) ;
}
if ( ! username & & ! io_data - > password )
* out_message = _ ( " A username and password are required. " ) ;
if ( io_data - > challenge_text )
* out_message = io_data - > challenge_text ;
}
handled = TRUE ;
} else if ( ! strcmp ( requested_auth , " Private Key " ) ) {
if ( io_data - > priv_key_pass ) {
char * qpass , * buf ;
/* Quote strings passed back to openvpn */
qpass = ovpn_quote_string ( io_data - > priv_key_pass ) ;
buf = g_strdup_printf ( " password \" %s \" \" %s \" \n " , requested_auth , qpass ) ;
memset ( qpass , 0 , strlen ( qpass ) ) ;
g_free ( qpass ) ;
/* Will always write everything in blocking channels (on success) */
g_io_channel_write_chars ( io_data - > socket_channel , buf , strlen ( buf ) , NULL , NULL ) ;
g_io_channel_flush ( io_data - > socket_channel , NULL ) ;
g_free ( buf ) ;
} else {
hints = g_new0 ( char * , 2 ) ;
hints [ i + + ] = NM_OPENVPN_KEY_CERTPASS ;
* out_message = _ ( " A private key password is required. " ) ;
}
handled = TRUE ;
} else if ( strcmp ( requested_auth , " HTTP Proxy " ) = = 0 ) {
if ( io_data - > proxy_username ! = NULL & & io_data - > proxy_password ! = NULL ) {
write_user_pass ( io_data - > socket_channel ,
requested_auth ,
io_data - > proxy_username ,
io_data - > proxy_password ) ;
} else {
hints = g_new0 ( char * , 3 ) ;
if ( ! io_data - > proxy_username ) {
hints [ i + + ] = NM_OPENVPN_KEY_HTTP_PROXY_USERNAME ;
* out_message = _ ( " An HTTP Proxy username is required. " ) ;
}
if ( ! io_data - > proxy_password ) {
hints [ i + + ] = NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD ;
* out_message = _ ( " An HTTP Proxy password is required. " ) ;
}
if ( ! io_data - > proxy_username & & ! io_data - > proxy_password )
* out_message = _ ( " An HTTP Proxy username and password are required. " ) ;
}
handled = TRUE ;
}
* out_hints = hints ;
return handled ;
}
static gboolean
2017-11-06 15:08:27 +03:00
handle_management_socket ( NMWireguardPlugin * plugin ,
2017-11-06 15:02:05 +03:00
GIOChannel * source ,
GIOCondition condition ,
NMVpnPluginFailure * out_failure )
{
2017-11-06 15:08:27 +03:00
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
2017-11-06 15:02:05 +03:00
gboolean again = TRUE ;
char * str = NULL , * auth = NULL ;
const char * message = NULL ;
char * * hints = NULL ;
g_assert ( out_failure ) ;
if ( ! ( condition & G_IO_IN ) )
return TRUE ;
if ( g_io_channel_read_line ( source , & str , NULL , NULL , NULL ) ! = G_IO_STATUS_NORMAL )
return TRUE ;
if ( ! str [ 0 ] ) {
g_free ( str ) ;
return TRUE ;
}
_LOGD ( " VPN request '%s' " , str ) ;
auth = get_detail ( str , " >PASSWORD:Need ' " ) ;
if ( auth ) {
if ( priv - > io_data - > pending_auth )
g_free ( priv - > io_data - > pending_auth ) ;
priv - > io_data - > pending_auth = auth ;
if ( handle_auth ( priv - > io_data , auth , & message , & hints ) ) {
/* Request new secrets if we need any */
if ( message ) {
if ( priv - > interactive ) {
gs_free char * joined = NULL ;
_LOGD ( " Requesting new secrets: '%s', %s%s%s " , message ,
NM_PRINT_FMT_QUOTED ( hints , " ( " , ( joined = g_strjoinv ( " , " , ( char * * ) hints ) ) , " ) " , " no hints " ) ) ;
nm_vpn_service_plugin_secrets_required ( ( NMVpnServicePlugin * ) plugin , message , ( const char * * ) hints ) ;
} else {
/* Interactive not allowed, can't ask for more secrets */
_LOGW ( " More secrets required but cannot ask interactively " ) ;
* out_failure = NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED ;
again = FALSE ;
}
}
if ( hints )
g_free ( hints ) ; /* elements are 'const' */
} else {
_LOGW ( " Unhandled management socket request '%s' " , auth ) ;
* out_failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED ;
again = FALSE ;
}
goto out ;
}
auth = get_detail ( str , " >PASSWORD:Verification Failed: ' " ) ;
if ( auth ) {
gboolean fail = TRUE ;
if ( ! strcmp ( auth , " Auth " ) ) {
gs_free char * failure_reason = NULL ;
failure_reason = get_detail ( auth , " >PASSWORD:Verification Failed: 'Auth' [' " ) ;
if ( parse_challenge ( failure_reason , & priv - > io_data - > challenge_state_id , & priv - > io_data - > challenge_text ) ) {
_LOGD ( " Received challenge '%s' for state '%s' " ,
priv - > io_data - > challenge_state_id ,
priv - > io_data - > challenge_text ) ;
} else
_LOGW ( " Password verification failed " ) ;
if ( priv - > interactive ) {
/* Clear existing password in interactive mode, openvpn
* will request a new one after restarting .
*/
if ( priv - > io_data - > password )
memset ( priv - > io_data - > password , 0 , strlen ( priv - > io_data - > password ) ) ;
g_clear_pointer ( & priv - > io_data - > password , g_free ) ;
fail = FALSE ;
}
} else if ( ! strcmp ( auth , " Private Key " ) )
_LOGW ( " Private key verification failed " ) ;
else
_LOGW ( " Unknown verification failed: %s " , auth ) ;
if ( fail ) {
* out_failure = NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED ;
again = FALSE ;
}
g_free ( auth ) ;
}
out :
g_free ( str ) ;
return again ;
}
static gboolean
nm_openvpn_socket_data_cb ( GIOChannel * source , GIOCondition condition , gpointer user_data )
{
2017-11-06 15:08:27 +03:00
NMWireguardPlugin * plugin = NM_WIREGUARD_PLUGIN ( user_data ) ;
2017-11-06 15:02:05 +03:00
NMVpnPluginFailure failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED ;
if ( ! handle_management_socket ( plugin , source , condition , & failure ) ) {
nm_vpn_service_plugin_failure ( ( NMVpnServicePlugin * ) plugin , failure ) ;
return FALSE ;
}
return TRUE ;
}
static gboolean
nm_openvpn_connect_timer_cb ( gpointer data )
{
2017-11-06 15:08:27 +03:00
NMWireguardPlugin * plugin = NM_WIREGUARD_PLUGIN ( data ) ;
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
NMWireguardPluginIOData * io_data = priv - > io_data ;
2017-11-06 15:02:05 +03:00
struct sockaddr_un remote = { 0 } ;
int fd ;
priv - > connect_count + + ;
/* open socket and start listener */
fd = socket ( AF_UNIX , SOCK_STREAM , 0 ) ;
if ( fd < 0 ) {
_LOGW ( " Could not create management socket " ) ;
nm_vpn_service_plugin_failure ( NM_VPN_SERVICE_PLUGIN ( plugin ) , NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED ) ;
goto out ;
}
remote . sun_family = AF_UNIX ;
g_strlcpy ( remote . sun_path , priv - > mgt_path , sizeof ( remote . sun_path ) ) ;
if ( connect ( fd , ( struct sockaddr * ) & remote , sizeof ( remote ) ) ! = 0 ) {
close ( fd ) ;
if ( priv - > connect_count < = 30 )
return G_SOURCE_CONTINUE ;
priv - > connect_timer = 0 ;
_LOGW ( " Could not open management socket " ) ;
nm_vpn_service_plugin_failure ( NM_VPN_SERVICE_PLUGIN ( plugin ) , NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED ) ;
} else {
io_data - > socket_channel = g_io_channel_unix_new ( fd ) ;
g_io_channel_set_encoding ( io_data - > socket_channel , NULL , NULL ) ;
io_data - > socket_channel_eventid = g_io_add_watch ( io_data - > socket_channel ,
G_IO_IN ,
nm_openvpn_socket_data_cb ,
plugin ) ;
}
out :
priv - > connect_timer = 0 ;
return G_SOURCE_REMOVE ;
}
static void
2017-11-06 15:08:27 +03:00
nm_openvpn_schedule_connect_timer ( NMWireguardPlugin * plugin )
2017-11-06 15:02:05 +03:00
{
2017-11-06 15:08:27 +03:00
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
2017-11-06 15:02:05 +03:00
if ( priv - > connect_timer = = 0 )
priv - > connect_timer = g_timeout_add ( 200 , nm_openvpn_connect_timer_cb , plugin ) ;
}
static void
2017-11-06 15:08:27 +03:00
openvpn_child_terminated ( NMWireguardPlugin * plugin , GPid pid , gint status )
2017-11-06 15:02:05 +03:00
{
2017-11-06 15:08:27 +03:00
NMWireguardPluginPrivate * priv ;
2017-11-06 15:02:05 +03:00
NMVpnPluginFailure failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED ;
gboolean good_exit = FALSE ;
2017-11-06 15:08:27 +03:00
g_return_if_fail ( NM_IS_WIREGUARD_PLUGIN ( plugin ) ) ;
2017-11-06 15:02:05 +03:00
2017-11-06 15:08:27 +03:00
priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
2017-11-06 15:02:05 +03:00
/* Reap child if needed. */
if ( priv - > pid ! = pid ) {
/* the dead child is not the currently active process. Nothing to do, we just
* reaped the PID . */
return ;
}
priv - > pid = 0 ;
/* OpenVPN doesn't supply useful exit codes :( */
if ( WIFEXITED ( status ) & & WEXITSTATUS ( status ) = = 0 )
good_exit = TRUE ;
/* Try to get the last bits of data from openvpn */
if ( priv - > io_data & & priv - > io_data - > socket_channel ) {
GIOChannel * channel = priv - > io_data - > socket_channel ;
GIOCondition condition ;
while ( ( condition = g_io_channel_get_buffer_condition ( channel ) ) & G_IO_IN ) {
if ( ! handle_management_socket ( plugin , channel , condition , & failure ) ) {
good_exit = FALSE ;
break ;
}
}
}
if ( good_exit )
nm_vpn_service_plugin_disconnect ( ( NMVpnServicePlugin * ) plugin , NULL ) ;
else
nm_vpn_service_plugin_failure ( ( NMVpnServicePlugin * ) plugin , failure ) ;
}
static gboolean
validate_auth ( const char * auth )
{
if ( auth ) {
if ( ! strcmp ( auth , NM_OPENVPN_AUTH_NONE )
| | ! strcmp ( auth , NM_OPENVPN_AUTH_RSA_MD4 )
| | ! strcmp ( auth , NM_OPENVPN_AUTH_MD5 )
| | ! strcmp ( auth , NM_OPENVPN_AUTH_SHA1 )
| | ! strcmp ( auth , NM_OPENVPN_AUTH_SHA224 )
| | ! strcmp ( auth , NM_OPENVPN_AUTH_SHA256 )
| | ! strcmp ( auth , NM_OPENVPN_AUTH_SHA384 )
| | ! strcmp ( auth , NM_OPENVPN_AUTH_SHA512 )
| | ! strcmp ( auth , NM_OPENVPN_AUTH_RIPEMD160 ) )
return TRUE ;
}
return FALSE ;
}
static const char *
validate_connection_type ( const char * ctype )
{
if ( ctype ) {
if ( ! strcmp ( ctype , NM_OPENVPN_CONTYPE_TLS )
| | ! strcmp ( ctype , NM_OPENVPN_CONTYPE_STATIC_KEY )
| | ! strcmp ( ctype , NM_OPENVPN_CONTYPE_PASSWORD )
| | ! strcmp ( ctype , NM_OPENVPN_CONTYPE_PASSWORD_TLS ) )
return ctype ;
}
return NULL ;
}
static gboolean
connection_type_is_tls_mode ( const char * connection_type )
{
return strcmp ( connection_type , NM_OPENVPN_CONTYPE_TLS ) = = 0
| | strcmp ( connection_type , NM_OPENVPN_CONTYPE_PASSWORD ) = = 0
| | strcmp ( connection_type , NM_OPENVPN_CONTYPE_PASSWORD_TLS ) = = 0 ;
}
static void
add_openvpn_arg ( GPtrArray * args , const char * arg )
{
g_return_if_fail ( args ! = NULL ) ;
g_return_if_fail ( arg ! = NULL ) ;
g_ptr_array_add ( args , g_strdup ( arg ) ) ;
}
static const char *
add_openvpn_arg_utf8safe ( GPtrArray * args , const char * arg )
{
char * arg_unescaped ;
g_return_val_if_fail ( args , NULL ) ;
g_return_val_if_fail ( arg , NULL ) ;
arg_unescaped = nm_utils_str_utf8safe_unescape_cp ( arg ) ;
g_ptr_array_add ( args , arg_unescaped ) ;
return arg_unescaped ;
}
static gboolean
add_openvpn_arg_int ( GPtrArray * args , const char * arg )
{
long int tmp_int ;
g_return_val_if_fail ( args ! = NULL , FALSE ) ;
g_return_val_if_fail ( arg ! = NULL , FALSE ) ;
/* Convert -> int and back to string for security's sake since
* strtol ( ) ignores some leading and trailing characters .
*/
errno = 0 ;
tmp_int = strtol ( arg , NULL , 10 ) ;
if ( errno ! = 0 )
return FALSE ;
g_ptr_array_add ( args , ( gpointer ) g_strdup_printf ( " %d " , ( guint32 ) tmp_int ) ) ;
return TRUE ;
}
static void
add_cert_args ( GPtrArray * args , NMSettingVpn * s_vpn )
{
const char * ca , * cert , * key ;
gs_free char * ca_free = NULL , * cert_free = NULL , * key_free = NULL ;
g_return_if_fail ( args ! = NULL ) ;
g_return_if_fail ( s_vpn ! = NULL ) ;
ca = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_CA ) ;
cert = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_CERT ) ;
key = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_KEY ) ;
ca = nm_utils_str_utf8safe_unescape ( ca , & ca_free ) ;
cert = nm_utils_str_utf8safe_unescape ( cert , & cert_free ) ;
key = nm_utils_str_utf8safe_unescape ( key , & key_free ) ;
if ( ca & & strlen ( ca )
& & cert & & strlen ( cert )
& & key & & strlen ( key )
& & ! strcmp ( ca , cert )
& & ! strcmp ( ca , key ) ) {
add_openvpn_arg ( args , " --pkcs12 " ) ;
add_openvpn_arg ( args , ca ) ;
} else {
if ( ca & & strlen ( ca ) ) {
add_openvpn_arg ( args , " --ca " ) ;
add_openvpn_arg ( args , ca ) ;
}
if ( cert & & strlen ( cert ) ) {
add_openvpn_arg ( args , " --cert " ) ;
add_openvpn_arg ( args , cert ) ;
}
if ( key & & strlen ( key ) ) {
add_openvpn_arg ( args , " --key " ) ;
add_openvpn_arg ( args , key ) ;
}
}
}
static void
2017-11-06 15:08:27 +03:00
update_io_data_from_vpn_setting ( NMWireguardPluginIOData * io_data ,
2017-11-06 15:02:05 +03:00
NMSettingVpn * s_vpn ,
const char * default_username )
{
const char * tmp ;
if ( default_username ) {
g_free ( io_data - > default_username ) ;
io_data - > default_username = g_strdup ( default_username ) ;
}
g_free ( io_data - > username ) ;
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_USERNAME ) ;
io_data - > username = tmp ? g_strdup ( tmp ) : NULL ;
if ( io_data - > password ) {
memset ( io_data - > password , 0 , strlen ( io_data - > password ) ) ;
g_free ( io_data - > password ) ;
}
tmp = nm_setting_vpn_get_secret ( s_vpn , NM_OPENVPN_KEY_PASSWORD ) ;
io_data - > password = tmp ? g_strdup ( tmp ) : NULL ;
if ( io_data - > priv_key_pass ) {
memset ( io_data - > priv_key_pass , 0 , strlen ( io_data - > priv_key_pass ) ) ;
g_free ( io_data - > priv_key_pass ) ;
}
tmp = nm_setting_vpn_get_secret ( s_vpn , NM_OPENVPN_KEY_CERTPASS ) ;
io_data - > priv_key_pass = tmp ? g_strdup ( tmp ) : NULL ;
g_free ( io_data - > proxy_username ) ;
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_HTTP_PROXY_USERNAME ) ;
io_data - > proxy_username = tmp ? g_strdup ( tmp ) : NULL ;
if ( io_data - > proxy_password ) {
memset ( io_data - > proxy_password , 0 , strlen ( io_data - > proxy_password ) ) ;
g_free ( io_data - > proxy_password ) ;
}
tmp = nm_setting_vpn_get_secret ( s_vpn , NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD ) ;
io_data - > proxy_password = tmp ? g_strdup ( tmp ) : NULL ;
}
static char *
mgt_path_create ( NMConnection * connection , GError * * error )
{
int errsv ;
/* Setup runtime directory */
if ( g_mkdir_with_parents ( RUNDIR , 0755 ) ! = 0 ) {
errsv = errno ;
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
" Cannot create run-dir %s (%s) " ,
RUNDIR , g_strerror ( errsv ) ) ;
return NULL ;
}
return g_strdup_printf ( RUNDIR " /nm-openvpn-%s " ,
nm_connection_get_uuid ( connection ) ) ;
}
# define MAX_GROUPS 128
static gboolean
is_dir_writable ( const char * dir , const char * user )
{
struct stat sb ;
struct passwd * pw ;
if ( stat ( dir , & sb ) = = - 1 )
return FALSE ;
pw = getpwnam ( user ) ;
if ( ! pw )
return FALSE ;
if ( pw - > pw_uid = = 0 )
return TRUE ;
if ( sb . st_mode & S_IWOTH )
return TRUE ;
else if ( sb . st_mode & S_IWGRP ) {
/* Group has write access. Is user in that group? */
int i , ngroups = MAX_GROUPS ;
gid_t groups [ MAX_GROUPS ] ;
getgrouplist ( user , pw - > pw_gid , groups , & ngroups ) ;
for ( i = 0 ; i < ngroups & & i < MAX_GROUPS ; i + + ) {
if ( groups [ i ] = = sb . st_gid )
return TRUE ;
}
} else if ( sb . st_mode & S_IWUSR ) {
/* The owner has write access. Does the user own the file? */
if ( pw - > pw_uid = = sb . st_uid )
return TRUE ;
}
return FALSE ;
}
/* Check existence of 'tmp' directory inside @chdir
* and write access in @ chdir and @ chdir / tmp for @ user .
*/
static gboolean
check_chroot_dir_usability ( const char * chdir , const char * user )
{
char * tmp_dir ;
gboolean b1 , b2 ;
tmp_dir = g_strdup_printf ( " %s/tmp " , chdir ) ;
if ( ! g_file_test ( tmp_dir , G_FILE_TEST_IS_DIR ) ) {
g_free ( tmp_dir ) ;
return FALSE ;
}
b1 = is_dir_writable ( chdir , user ) ;
b2 = is_dir_writable ( tmp_dir , user ) ;
g_free ( tmp_dir ) ;
return b1 & & b2 ;
}
static gboolean
2017-11-06 15:08:27 +03:00
nm_wireguard_start_interface ( NMWireguardPlugin * plugin ,
NMConnection * connection ,
GError * * error )
{
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
const char * wg_connection_name = NULL ;
return TRUE ;
}
static gboolean
nm_openvpn_start_openvpn_binary ( NMWireguardPlugin * plugin ,
2017-11-06 15:02:05 +03:00
NMConnection * connection ,
GError * * error )
{
2017-11-06 15:08:27 +03:00
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
2017-11-06 15:02:05 +03:00
const char * openvpn_binary , * auth , * tmp , * tmp2 , * tmp3 , * tmp4 ;
gs_unref_ptrarray GPtrArray * args = NULL ;
GPid pid ;
gboolean dev_type_is_tap ;
char * stmp ;
const char * defport , * proto_tcp ;
const char * tls_remote = NULL ;
const char * nm_openvpn_user , * nm_openvpn_group , * nm_openvpn_chroot ;
gs_free char * bus_name = NULL ;
NMSettingVpn * s_vpn ;
const char * connection_type ;
gint64 v_int64 ;
char sbuf_64 [ 65 ] ;
OpenvpnBinaryVersion openvpn_binary_version = OPENVPN_BINARY_VERSION_INVALID ;
s_vpn = nm_connection_get_setting_vpn ( connection ) ;
if ( ! s_vpn ) {
g_set_error_literal ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION ,
_ ( " Could not process the request because the VPN connection settings were invalid. " ) ) ;
return FALSE ;
}
connection_type = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_CONNECTION_TYPE ) ;
if ( ! validate_connection_type ( connection_type ) ) {
g_set_error_literal ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid connection type. " ) ) ;
return FALSE ;
}
/* Validate the properties */
if ( ! nm_openvpn_properties_validate ( s_vpn , error ) )
return FALSE ;
/* Validate secrets */
if ( ! nm_openvpn_secrets_validate ( s_vpn , error ) )
return FALSE ;
/* Find openvpn */
openvpn_binary = openvpn_binary_find_exepath ( ) ;
if ( ! openvpn_binary ) {
g_set_error_literal ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Could not find the openvpn binary. " ) ) ;
return FALSE ;
}
auth = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_AUTH ) ;
if ( auth ) {
if ( ! validate_auth ( auth ) ) {
g_set_error_literal ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid HMAC auth. " ) ) ;
return FALSE ;
}
}
args = g_ptr_array_new_with_free_func ( g_free ) ;
add_openvpn_arg ( args , openvpn_binary ) ;
defport = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_PORT ) ;
if ( defport & & ! defport [ 0 ] )
defport = NULL ;
proto_tcp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_PROTO_TCP ) ;
if ( proto_tcp & & ! proto_tcp [ 0 ] )
proto_tcp = NULL ;
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_REMOTE ) ;
if ( tmp & & * tmp ) {
gs_free char * tmp_clone = NULL ;
char * tmp_remaining ;
const char * tok ;
tmp_remaining = tmp_clone = g_strdup ( tmp ) ;
while ( ( tok = strsep ( & tmp_remaining , " \t , " ) ) ! = NULL ) {
gs_free char * str_free = NULL ;
const char * host , * port , * proto ;
gssize eidx ;
eidx = nmovpn_remote_parse ( tok ,
& str_free ,
& host ,
& port ,
& proto ,
NULL ) ;
if ( eidx > = 0 )
continue ;
add_openvpn_arg ( args , " --remote " ) ;
add_openvpn_arg ( args , host ) ;
if ( port ) {
if ( ! add_openvpn_arg_int ( args , port ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid port number “%s”. " ) , port ) ;
return FALSE ;
}
} else if ( defport ) {
if ( ! add_openvpn_arg_int ( args , defport ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid port number “%s”. " ) ,
defport ) ;
return FALSE ;
}
} else
add_openvpn_arg ( args , " 1194 " ) ; /* default IANA port */
if ( proto ) {
if ( nm_streq ( proto , " tcp " ) )
add_openvpn_arg ( args , " tcp-client " ) ;
else if ( nm_streq ( proto , " tcp4 " ) )
add_openvpn_arg ( args , " tcp4-client " ) ;
else if ( nm_streq ( proto , " tcp6 " ) )
add_openvpn_arg ( args , " tcp6-client " ) ;
else if ( NM_IN_STRSET ( proto , NMOVPN_PROTCOL_TYPES ) )
add_openvpn_arg ( args , proto ) ;
else {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid proto “%s”. " ) , proto ) ;
return FALSE ;
}
} else if ( proto_tcp & & ! strcmp ( proto_tcp , " yes " ) )
add_openvpn_arg ( args , " tcp-client " ) ;
else
add_openvpn_arg ( args , " udp " ) ;
}
}
/* Remote random */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_REMOTE_RANDOM ) ;
if ( tmp & & ! strcmp ( tmp , " yes " ) )
add_openvpn_arg ( args , " --remote-random " ) ;
/* tun-ipv6 */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_TUN_IPV6 ) ;
if ( tmp & & ! strcmp ( tmp , " yes " ) )
add_openvpn_arg ( args , " --tun-ipv6 " ) ;
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_PROXY_TYPE ) ;
tmp2 = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_PROXY_SERVER ) ;
tmp3 = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_PROXY_PORT ) ;
tmp4 = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_PROXY_RETRY ) ;
if ( tmp & & strlen ( tmp ) & & tmp2 & & strlen ( tmp2 ) ) {
if ( ! strcmp ( tmp , " http " ) ) {
add_openvpn_arg ( args , " --http-proxy " ) ;
add_openvpn_arg ( args , tmp2 ) ;
add_openvpn_arg ( args , tmp3 ? tmp3 : " 8080 " ) ;
add_openvpn_arg ( args , " auto " ) ; /* Automatic proxy auth method detection */
if ( tmp4 )
add_openvpn_arg ( args , " --http-proxy-retry " ) ;
} else if ( ! strcmp ( tmp , " socks " ) ) {
add_openvpn_arg ( args , " --socks-proxy " ) ;
add_openvpn_arg ( args , tmp2 ) ;
add_openvpn_arg ( args , tmp3 ? tmp3 : " 1080 " ) ;
if ( tmp4 )
add_openvpn_arg ( args , " --socks-proxy-retry " ) ;
} else {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid proxy type “%s”. " ) ,
tmp ) ;
return FALSE ;
}
}
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_COMP_LZO ) ;
/* openvpn understands 4 different modes for --comp-lzo, which have
* different meaning :
* 1 ) no - - comp - lzo option
* 2 ) - - comp - lzo yes
* 3 ) - - comp - lzo [ adaptive ]
* 4 ) - - comp - lzo no
*
* In the past , nm - openvpn only supported 1 ) and 2 ) by having no
* comp - lzo connection setting or " comp-lzo=yes " , respectively .
*
* However , old plasma - nm would set " comp-lzo=no " in the connection
* to mean 1 ) . Thus , " comp-lzo=no " is spoiled to mean 4 ) in order
* to preserve backward compatibily .
* We use instead a special value " no-by-default " to express " no " .
*
* See bgo # 769177
*/
if ( NM_IN_STRSET ( tmp , " no " ) ) {
/* means no --comp-lzo option. */
tmp = NULL ;
} else if ( NM_IN_STRSET ( tmp , " no-by-default " ) )
tmp = " no " ;
if ( NM_IN_STRSET ( tmp , " yes " , " no " , " adaptive " ) ) {
add_openvpn_arg ( args , " --comp-lzo " ) ;
add_openvpn_arg ( args , tmp ) ;
}
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_FLOAT ) ;
if ( tmp & & ! strcmp ( tmp , " yes " ) )
add_openvpn_arg ( args , " --float " ) ;
/* ping, ping-exit, ping-restart */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_PING ) ;
if ( tmp ) {
add_openvpn_arg ( args , " --ping " ) ;
if ( ! add_openvpn_arg_int ( args , tmp ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid ping duration “%s”. " ) ,
tmp ) ;
return FALSE ;
}
}
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_PING_EXIT ) ;
if ( tmp ) {
add_openvpn_arg ( args , " --ping-exit " ) ;
if ( ! add_openvpn_arg_int ( args , tmp ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid ping-exit duration “%s”. " ) ,
tmp ) ;
return FALSE ;
}
}
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_PING_RESTART ) ;
if ( tmp ) {
add_openvpn_arg ( args , " --ping-restart " ) ;
if ( ! add_openvpn_arg_int ( args , tmp ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid ping-restart duration “%s”. " ) ,
tmp ) ;
return FALSE ;
}
}
add_openvpn_arg ( args , " --nobind " ) ;
/* max routes allowed from openvpn server */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_MAX_ROUTES ) ;
if ( tmp ) {
/* max-routes option is deprecated in 2.4 release
* https : //github.com/OpenVPN/openvpn/commit/d0085293e709c8a722356cfa68ad74c962aef9a2
*/
add_openvpn_arg ( args , " --max-routes " ) ;
if ( ! add_openvpn_arg_int ( args , tmp ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid max-routes argument “%s”. " ) ,
tmp ) ;
return FALSE ;
}
}
/* Device and device type, defaults to tun */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_DEV ) ;
tmp2 = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_DEV_TYPE ) ;
tmp3 = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_TAP_DEV ) ;
add_openvpn_arg ( args , " --dev " ) ;
if ( tmp ) {
const char * tmp_unescaped ;
tmp_unescaped = add_openvpn_arg_utf8safe ( args , tmp ) ;
dev_type_is_tap = g_str_has_prefix ( tmp_unescaped , " tap " ) ;
} else if ( tmp2 ) {
add_openvpn_arg ( args , tmp2 ) ;
dev_type_is_tap = FALSE ; /* will be reset below (avoid maybe-uninitialized warning) */
} else if ( tmp3 & & ! strcmp ( tmp3 , " yes " ) ) {
add_openvpn_arg ( args , " tap " ) ;
dev_type_is_tap = TRUE ;
} else {
add_openvpn_arg ( args , " tun " ) ;
dev_type_is_tap = FALSE ;
}
/* Add '--dev-type' if the type was explicitly set */
if ( tmp2 ) {
add_openvpn_arg ( args , " --dev-type " ) ;
add_openvpn_arg ( args , tmp2 ) ;
dev_type_is_tap = ( strcmp ( tmp2 , " tap " ) = = 0 ) ;
}
/* Cipher */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_CIPHER ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --cipher " ) ;
add_openvpn_arg ( args , tmp ) ;
}
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_TLS_CIPHER ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --tls-cipher " ) ;
add_openvpn_arg ( args , tmp ) ;
}
/* Keysize */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_KEYSIZE ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --keysize " ) ;
if ( ! add_openvpn_arg_int ( args , tmp ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid keysize “%s”. " ) ,
tmp ) ;
return FALSE ;
}
}
/* Auth */
if ( auth ) {
add_openvpn_arg ( args , " --auth " ) ;
add_openvpn_arg ( args , auth ) ;
}
add_openvpn_arg ( args , " --auth-nocache " ) ;
/* tls-auth */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_TA ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --tls-auth " ) ;
add_openvpn_arg_utf8safe ( args , tmp ) ;
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_TA_DIR ) ;
if ( tmp & & tmp [ 0 ] )
add_openvpn_arg ( args , tmp ) ;
}
/* tls-crypt */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_TLS_CRYPT ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --tls-crypt " ) ;
add_openvpn_arg_utf8safe ( args , tmp ) ;
}
/* tls-remote */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_TLS_REMOTE ) ;
if ( tmp & & tmp [ 0 ] ) {
if ( openvpn_binary_detect_version_cached ( openvpn_binary , & openvpn_binary_version ) ! = OPENVPN_BINARY_VERSION_2_4_OR_NEWER ) {
_LOGW ( " the tls-remote option is deprecated and removed from OpenVPN 2.4. Update your connection to use verify-x509-name " ) ;
add_openvpn_arg ( args , " --tls-remote " ) ;
add_openvpn_arg ( args , tmp ) ;
} else {
_LOGW ( " the tls-remote option is deprecated and removed from OpenVPN 2.4. For compatibility, the plugin uses \" verify-x509-name \" \" %s \" \" name \" instead. Update your connection to use verify-x509-name " , tmp ) ;
add_openvpn_arg ( args , " --verify-x509-name " ) ;
add_openvpn_arg ( args , tmp ) ;
add_openvpn_arg ( args , " name " ) ;
}
tls_remote = tmp ;
}
/* verify-x509-name */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_VERIFY_X509_NAME ) ;
if ( tmp & & tmp [ 0 ] ) {
const char * name ;
gs_free char * type = NULL ;
if ( tls_remote ) {
g_set_error ( error , NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid configuration with tls-remote and verify-x509-name. " ) ) ;
return FALSE ;
}
name = strchr ( tmp , ' : ' ) ;
if ( name ) {
type = g_strndup ( tmp , name - tmp ) ;
name + + ;
} else
name = tmp ;
if ( ! name [ 0 ] | | ! g_utf8_validate ( name , - 1 , NULL ) ) {
g_set_error ( error , NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid verify-x509-name. " ) ) ;
return FALSE ;
}
add_openvpn_arg ( args , " --verify-x509-name " ) ;
add_openvpn_arg ( args , name ) ;
add_openvpn_arg ( args , type ? : " subject " ) ;
}
/* remote-cert-tls */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_REMOTE_CERT_TLS ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --remote-cert-tls " ) ;
add_openvpn_arg ( args , tmp ) ;
}
/* ns-cert-type */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_NS_CERT_TYPE ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --ns-cert-type " ) ;
add_openvpn_arg ( args , tmp ) ;
}
/* Reneg seconds */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_RENEG_SECONDS ) ;
if ( ! connection_type_is_tls_mode ( connection_type ) ) {
/* Ignore --reneg-sec option if we are not in TLS mode (as enabled
* by - - client below ) . openvpn will error out otherwise , see bgo # 749050. */
} else if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --reneg-sec " ) ;
if ( ! add_openvpn_arg_int ( args , tmp ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid reneg seconds “%s”. " ) ,
tmp ) ;
return FALSE ;
}
} else {
/* Either the server and client must agree on the renegotiation
* interval , or it should be disabled on one side to prevent
* too - frequent renegotiations , which make two - factor auth quite
* painful .
*/
add_openvpn_arg ( args , " --reneg-sec " ) ;
add_openvpn_arg ( args , " 0 " ) ;
}
if ( gl . log_level_ovpn > = 0 ) {
add_openvpn_arg ( args , " --verb " ) ;
add_openvpn_arg ( args , nm_sprintf_buf ( sbuf_64 , " %d " , gl . log_level_ovpn ) ) ;
}
if ( gl . log_syslog ) {
add_openvpn_arg ( args , " --syslog " ) ;
add_openvpn_arg ( args , " nm-openvpn " ) ;
}
/* TUN MTU size */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_TUNNEL_MTU ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --tun-mtu " ) ;
if ( ! add_openvpn_arg_int ( args , tmp ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid TUN MTU size “%s”. " ) ,
tmp ) ;
return FALSE ;
}
}
/* fragment size */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_FRAGMENT_SIZE ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --fragment " ) ;
if ( ! add_openvpn_arg_int ( args , tmp ) ) {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid fragment size “%s”. " ) ,
tmp ) ;
return FALSE ;
}
}
/* mssfix */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_MSSFIX ) ;
if ( tmp ) {
if ( nm_streq ( tmp , " yes " ) )
add_openvpn_arg ( args , " --mssfix " ) ;
else if ( ( v_int64 = _nm_utils_ascii_str_to_int64 ( tmp , 10 , 1 , G_MAXINT32 , 0 ) ) ) {
add_openvpn_arg ( args , " --mssfix " ) ;
add_openvpn_arg ( args , nm_sprintf_buf ( sbuf_64 , " %d " , ( int ) v_int64 ) ) ;
}
}
/* mtu-disc */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_MTU_DISC ) ;
if ( NM_IN_STRSET ( tmp , " no " , " maybe " , " yes " ) ) {
add_openvpn_arg ( args , " --mtu-disc " ) ;
add_openvpn_arg ( args , tmp ) ;
}
/* ifconfig */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_LOCAL_IP ) ;
tmp2 = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_REMOTE_IP ) ;
if ( tmp & & tmp2 ) {
add_openvpn_arg ( args , " --ifconfig " ) ;
add_openvpn_arg ( args , tmp ) ;
add_openvpn_arg ( args , tmp2 ) ;
}
/* Punch script security in the face; this option was added to OpenVPN 2.1-rc9
* and defaults to disallowing any scripts , a behavior change from previous
* versions .
*/
add_openvpn_arg ( args , " --script-security " ) ;
add_openvpn_arg ( args , " 2 " ) ;
/* Up script, called when connection has been established or has been restarted */
add_openvpn_arg ( args , " --up " ) ;
g_object_get ( plugin , NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME , & bus_name , NULL ) ;
stmp = g_strdup_printf ( " %s --debug %d %ld --bus-name %s %s -- " ,
NM_OPENVPN_HELPER_PATH ,
gl . log_level , ( long ) getpid ( ) ,
bus_name ,
dev_type_is_tap ? " --tap " : " --tun " ) ;
add_openvpn_arg ( args , stmp ) ;
g_free ( stmp ) ;
add_openvpn_arg ( args , " --up-restart " ) ;
/* Keep key and tun if restart is needed */
add_openvpn_arg ( args , " --persist-key " ) ;
add_openvpn_arg ( args , " --persist-tun " ) ;
/* Management socket for localhost access to supply username and password */
g_clear_pointer ( & priv - > mgt_path , g_free ) ;
priv - > mgt_path = mgt_path_create ( connection , error ) ;
if ( ! priv - > mgt_path )
return FALSE ;
add_openvpn_arg ( args , " --management " ) ;
add_openvpn_arg ( args , priv - > mgt_path ) ;
add_openvpn_arg ( args , " unix " ) ;
add_openvpn_arg ( args , " --management-client-user " ) ;
add_openvpn_arg ( args , " root " ) ;
add_openvpn_arg ( args , " --management-client-group " ) ;
add_openvpn_arg ( args , " root " ) ;
/* Query on the management socket for user/pass */
add_openvpn_arg ( args , " --management-query-passwords " ) ;
add_openvpn_arg ( args , " --auth-retry " ) ;
add_openvpn_arg ( args , " interact " ) ;
/* do not let openvpn setup routes or addresses, NM will handle it */
add_openvpn_arg ( args , " --route-noexec " ) ;
add_openvpn_arg ( args , " --ifconfig-noexec " ) ;
/* Now append configuration options which are dependent on the configuration type */
if ( ! strcmp ( connection_type , NM_OPENVPN_CONTYPE_TLS ) ) {
add_openvpn_arg ( args , " --client " ) ;
add_cert_args ( args , s_vpn ) ;
} else if ( ! strcmp ( connection_type , NM_OPENVPN_CONTYPE_STATIC_KEY ) ) {
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_STATIC_KEY ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --secret " ) ;
add_openvpn_arg_utf8safe ( args , tmp ) ;
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_STATIC_KEY_DIRECTION ) ;
if ( tmp & & tmp [ 0 ] )
add_openvpn_arg ( args , tmp ) ;
}
} else if ( ! strcmp ( connection_type , NM_OPENVPN_CONTYPE_PASSWORD ) ) {
/* Client mode */
add_openvpn_arg ( args , " --client " ) ;
/* Use user/path authentication */
add_openvpn_arg ( args , " --auth-user-pass " ) ;
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_CA ) ;
if ( tmp & & tmp [ 0 ] ) {
add_openvpn_arg ( args , " --ca " ) ;
add_openvpn_arg_utf8safe ( args , tmp ) ;
}
} else if ( ! strcmp ( connection_type , NM_OPENVPN_CONTYPE_PASSWORD_TLS ) ) {
add_openvpn_arg ( args , " --client " ) ;
add_cert_args ( args , s_vpn ) ;
/* Use user/path authentication */
add_openvpn_arg ( args , " --auth-user-pass " ) ;
} else {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Unknown connection type “%s”. " ) ,
connection_type ) ;
return FALSE ;
}
/* Allow openvpn to be run as a specified user:group.
*
* We do this by default . The only way to disable it is by setting
* empty environment variables NM_OPENVPN_USER and NM_OPENVPN_GROUP . */
nm_openvpn_user = getenv ( " NM_OPENVPN_USER " ) ? : NM_OPENVPN_USER ;
nm_openvpn_group = getenv ( " NM_OPENVPN_GROUP " ) ? : NM_OPENVPN_GROUP ;
if ( * nm_openvpn_user ) {
if ( getpwnam ( nm_openvpn_user ) ) {
add_openvpn_arg ( args , " --user " ) ;
add_openvpn_arg ( args , nm_openvpn_user ) ;
} else {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " User “%s” not found, check NM_OPENVPN_USER. " ) ,
nm_openvpn_user ) ;
return FALSE ;
}
}
if ( * nm_openvpn_group ) {
if ( getgrnam ( nm_openvpn_group ) ) {
add_openvpn_arg ( args , " --group " ) ;
add_openvpn_arg ( args , nm_openvpn_group ) ;
} else {
g_set_error ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Group “%s” not found, check NM_OPENVPN_GROUP. " ) ,
nm_openvpn_group ) ;
return FALSE ;
}
}
/* we try to chroot be default. The only way to disable that is by
* setting the an empty environment variable NM_OPENVPN_CHROOT . */
nm_openvpn_chroot = getenv ( " NM_OPENVPN_CHROOT " ) ? : NM_OPENVPN_CHROOT ;
if ( * nm_openvpn_chroot ) {
if ( check_chroot_dir_usability ( nm_openvpn_chroot , nm_openvpn_user ) ) {
add_openvpn_arg ( args , " --chroot " ) ;
add_openvpn_arg ( args , nm_openvpn_chroot ) ;
} else
_LOGW ( " Directory '%s' not usable for chroot by '%s', openvpn will not be chrooted. " ,
nm_openvpn_chroot , nm_openvpn_user ) ;
}
g_ptr_array_add ( args , NULL ) ;
{
gs_free char * cmd = NULL ;
_LOGD ( " EXEC: '%s' " , ( cmd = g_strjoinv ( " " , ( char * * ) args - > pdata ) ) ) ;
}
if ( ! g_spawn_async ( NULL , ( char * * ) args - > pdata , NULL ,
G_SPAWN_DO_NOT_REAP_CHILD , NULL , NULL , & pid , error ) )
return FALSE ;
pids_pending_add ( pid , plugin ) ;
g_warn_if_fail ( ! priv - > pid ) ;
priv - > pid = pid ;
/* Listen to the management socket for a few connection types:
PASSWORD : Will require username and password
X509USERPASS : Will require username and password and maybe certificate password
X509 : May require certificate password
*/
if ( ! strcmp ( connection_type , NM_OPENVPN_CONTYPE_TLS )
| | ! strcmp ( connection_type , NM_OPENVPN_CONTYPE_PASSWORD )
| | ! strcmp ( connection_type , NM_OPENVPN_CONTYPE_PASSWORD_TLS )
| | nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_HTTP_PROXY_USERNAME ) ) {
2017-11-06 15:08:27 +03:00
priv - > io_data = g_malloc0 ( sizeof ( NMWireguardPluginIOData ) ) ;
2017-11-06 15:02:05 +03:00
update_io_data_from_vpn_setting ( priv - > io_data , s_vpn ,
nm_setting_vpn_get_user_name ( s_vpn ) ) ;
nm_openvpn_schedule_connect_timer ( plugin ) ;
}
return TRUE ;
}
static const char *
check_need_secrets ( NMSettingVpn * s_vpn , gboolean * need_secrets )
{
const char * tmp , * key , * ctype ;
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE ;
gs_free char * key_free = NULL ;
g_return_val_if_fail ( s_vpn ! = NULL , FALSE ) ;
g_return_val_if_fail ( need_secrets ! = NULL , FALSE ) ;
* need_secrets = FALSE ;
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_CONNECTION_TYPE ) ;
ctype = validate_connection_type ( tmp ) ;
if ( ! ctype )
return NULL ;
if ( ! strcmp ( ctype , NM_OPENVPN_CONTYPE_PASSWORD_TLS ) ) {
/* Will require a password and maybe private key password */
key = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_KEY ) ;
key = nm_utils_str_utf8safe_unescape ( key , & key_free ) ;
if ( is_encrypted ( key ) & & ! nm_setting_vpn_get_secret ( s_vpn , NM_OPENVPN_KEY_CERTPASS ) )
* need_secrets = TRUE ;
if ( ! nm_setting_vpn_get_secret ( s_vpn , NM_OPENVPN_KEY_PASSWORD ) ) {
* need_secrets = TRUE ;
if ( nm_setting_get_secret_flags ( NM_SETTING ( s_vpn ) , NM_OPENVPN_KEY_PASSWORD , & secret_flags , NULL ) ) {
if ( secret_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED )
* need_secrets = FALSE ;
}
}
} else if ( ! strcmp ( ctype , NM_OPENVPN_CONTYPE_PASSWORD ) ) {
/* Will require a password */
if ( ! nm_setting_vpn_get_secret ( s_vpn , NM_OPENVPN_KEY_PASSWORD ) ) {
* need_secrets = TRUE ;
if ( nm_setting_get_secret_flags ( NM_SETTING ( s_vpn ) , NM_OPENVPN_KEY_PASSWORD , & secret_flags , NULL ) ) {
if ( secret_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED )
* need_secrets = FALSE ;
}
}
} else if ( ! strcmp ( ctype , NM_OPENVPN_CONTYPE_TLS ) ) {
/* May require private key password */
key = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_KEY ) ;
key = nm_utils_str_utf8safe_unescape ( key , & key_free ) ;
if ( is_encrypted ( key ) & & ! nm_setting_vpn_get_secret ( s_vpn , NM_OPENVPN_KEY_CERTPASS ) )
* need_secrets = TRUE ;
} else {
/* Static key doesn't need passwords */
}
/* HTTP Proxy might require a password; assume so if there's an HTTP proxy username */
tmp = nm_setting_vpn_get_data_item ( s_vpn , NM_OPENVPN_KEY_HTTP_PROXY_USERNAME ) ;
if ( tmp & & ! nm_setting_vpn_get_secret ( s_vpn , NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD ) )
* need_secrets = TRUE ;
return ctype ;
}
2017-11-06 15:08:27 +03:00
// IMPLEMENT ME RIGHT
static gboolean
test_disconnect ( NMVpnServicePlugin * plugin ,
GError * * err )
{
_LOGI ( " Did a disconnect! " ) ;
return TRUE ;
}
2017-11-06 15:02:05 +03:00
static gboolean
real_disconnect ( NMVpnServicePlugin * plugin ,
GError * * err )
{
2017-11-06 15:08:27 +03:00
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
2017-11-06 15:02:05 +03:00
if ( priv - > mgt_path ) {
/* openvpn does not cleanup the management socket upon exit,
* possibly it could not even because it changed user */
( void ) unlink ( priv - > mgt_path ) ;
g_clear_pointer ( & priv - > mgt_path , g_free ) ;
}
if ( priv - > pid ) {
pids_pending_send_sigterm ( priv - > pid ) ;
priv - > pid = 0 ;
}
return TRUE ;
}
2017-11-06 15:08:27 +03:00
// IMPLEMENT ME RIGHT
static gboolean
test_connect ( NMVpnServicePlugin * plugin ,
NMConnection * connection ,
2017-12-20 15:00:13 +03:00
GError * * err )
2017-11-06 15:08:27 +03:00
{
2017-12-20 15:00:13 +03:00
NMSettingVpn * setting = nm_connection_get_setting_vpn ( connection ) ;
char * secret = nm_setting_vpn_get_secret ( setting , " name " ) ;
char * str_setting = nm_setting_to_string ( setting ) ;
char * wg_quick_path = wg_quick_find_exepath ( ) ;
//char *
if ( wg_quick_path = = NULL ) {
_LOGW ( " Error: Could not find wg-quick! " ) ;
return FALSE ;
}
printf ( " Setting to String: %s \n " , str_setting ) ;
printf ( " Secret: %s \n " , secret ) ;
//char **cmd = {wg_quick_path, "up", "mullvad", NULL};
char * * cmd = { " /usr/bin/touch " , " /home/maxmanski/uwotm8 " , NULL } ;
2017-11-06 15:08:27 +03:00
GPid pid = 0 ;
GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD ;
2017-12-20 15:00:13 +03:00
spawn_flags = G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL ;
spawn_flags = G_SPAWN_DEFAULT | G_SPAWN_DO_NOT_REAP_CHILD ;
// if (!g_spawn_async (NULL, cmd, NULL, spawn_flags, NULL, NULL, &pid, err)){
int exit_code = 0 ;
* err = NULL ; // TODO remove?
2017-11-06 15:08:27 +03:00
2017-12-20 15:00:13 +03:00
if ( ! g_spawn_async ( NULL , cmd , NULL , spawn_flags , NULL , NULL , & pid , err ) ) {
_LOGW ( " An error occured while spawning wg-quick! (Error: %s) " , ( * err ) - > message ) ;
2017-11-06 15:08:27 +03:00
return FALSE ;
}
2017-12-20 15:00:13 +03:00
pids_pending_add_wg ( pid , plugin ) ;
printf ( " PID of spawned command: %d \n " , pid ) ;
// note: exit code 139 means SIGSEV (program died because of segfault or so)
{
printf ( " WG set up and ready to go! \n " ) ;
}
_LOGI ( " Did a dummy connect " ) ;
2017-11-06 15:08:27 +03:00
return TRUE ;
}
// IMPLEMENT ME RIGHT
static gboolean
test_connect_interactive ( NMVpnServicePlugin * plugin ,
NMConnection * connection ,
GError * * error ) {
_LOGI ( " Did an interactive dummy connect " ) ;
return TRUE ;
}
2017-11-06 15:02:05 +03:00
static gboolean
_connect_common ( NMVpnServicePlugin * plugin ,
NMConnection * connection ,
GVariant * details ,
GError * * error )
{
GError * local = NULL ;
if ( ! real_disconnect ( plugin , & local ) ) {
_LOGW ( " Could not clean up previous daemon run: %s " , local - > message ) ;
g_error_free ( local ) ;
}
2017-11-06 15:08:27 +03:00
return nm_openvpn_start_openvpn_binary ( NM_WIREGUARD_PLUGIN ( plugin ) ,
2017-11-06 15:02:05 +03:00
connection ,
error ) ;
}
static gboolean
real_connect ( NMVpnServicePlugin * plugin ,
NMConnection * connection ,
GError * * error )
{
return _connect_common ( plugin , connection , NULL , error ) ;
}
static gboolean
real_connect_interactive ( NMVpnServicePlugin * plugin ,
NMConnection * connection ,
GVariant * details ,
GError * * error )
{
if ( ! _connect_common ( plugin , connection , details , error ) )
return FALSE ;
2017-11-06 15:08:27 +03:00
NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) - > interactive = TRUE ;
2017-11-06 15:02:05 +03:00
return TRUE ;
}
2017-11-06 15:08:27 +03:00
// IMPLEMENT ME RIGHT
static gboolean
test_need_secrets ( NMVpnServicePlugin * plugin ,
NMConnection * connection ,
const char * * setting_name ,
GError * * error )
{
_LOGI ( " I require no secrets! " ) ;
return FALSE ;
}
2017-11-06 15:02:05 +03:00
static gboolean
real_need_secrets ( NMVpnServicePlugin * plugin ,
NMConnection * connection ,
const char * * setting_name ,
GError * * error )
{
NMSettingVpn * s_vpn ;
const char * connection_type ;
gboolean need_secrets = FALSE ;
g_return_val_if_fail ( NM_IS_VPN_SERVICE_PLUGIN ( plugin ) , FALSE ) ;
g_return_val_if_fail ( NM_IS_CONNECTION ( connection ) , FALSE ) ;
if ( _LOGD_enabled ( ) ) {
_LOGD ( " connection ------------------------------------- " ) ;
nm_connection_dump ( connection ) ;
}
s_vpn = nm_connection_get_setting_vpn ( connection ) ;
if ( ! s_vpn ) {
g_set_error_literal ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION ,
_ ( " Could not process the request because the VPN connection settings were invalid. " ) ) ;
return FALSE ;
}
connection_type = check_need_secrets ( s_vpn , & need_secrets ) ;
if ( ! connection_type ) {
g_set_error_literal ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS ,
_ ( " Invalid connection type. " ) ) ;
return FALSE ;
}
if ( need_secrets )
* setting_name = NM_SETTING_VPN_SETTING_NAME ;
return need_secrets ;
}
2017-11-06 15:08:27 +03:00
// IMPLEMENT ME RIGHT
static gboolean
test_new_secrets ( NMVpnServicePlugin * plugin ,
NMConnection * connection ,
GError * * error )
{
_LOGI ( " New Secrets, anyone? " ) ;
return TRUE ;
}
2017-11-06 15:02:05 +03:00
static gboolean
real_new_secrets ( NMVpnServicePlugin * plugin ,
NMConnection * connection ,
GError * * error )
{
2017-11-06 15:08:27 +03:00
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
2017-11-06 15:02:05 +03:00
NMSettingVpn * s_vpn ;
const char * message = NULL ;
char * * hints = NULL ;
s_vpn = nm_connection_get_setting_vpn ( connection ) ;
if ( ! s_vpn ) {
g_set_error_literal ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION ,
_ ( " Could not process the request because the VPN connection settings were invalid. " ) ) ;
return FALSE ;
}
_LOGD ( " VPN received new secrets; sending to management interface " ) ;
update_io_data_from_vpn_setting ( priv - > io_data , s_vpn , NULL ) ;
g_warn_if_fail ( priv - > io_data - > pending_auth ) ;
if ( ! handle_auth ( priv - > io_data , priv - > io_data - > pending_auth , & message , & hints ) ) {
g_set_error_literal ( error ,
NM_VPN_PLUGIN_ERROR ,
NM_VPN_PLUGIN_ERROR_FAILED ,
_ ( " Unhandled pending authentication. " ) ) ;
return FALSE ;
}
/* Request new secrets if we need any */
if ( message ) {
_LOGD ( " Requesting new secrets: '%s' " , message ) ;
nm_vpn_service_plugin_secrets_required ( plugin , message , ( const char * * ) hints ) ;
}
if ( hints )
g_free ( hints ) ; /* elements are 'const' */
return TRUE ;
}
static void
2017-11-06 15:08:27 +03:00
nm_wireguard_plugin_init ( NMWireguardPlugin * plugin )
2017-11-06 15:02:05 +03:00
{
2017-11-06 15:08:27 +03:00
// FIXME this is only for testing if the function gets called
GPid pid = 0 ;
GError * error = NULL ;
char * * cmd = malloc ( sizeof ( char * ) * 3 ) ;
cmd [ 0 ] = " /usr/bin/touch " ;
cmd [ 1 ] = " /home/maxmanski/givemeyournumber " ;
cmd [ 2 ] = NULL ;
GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD ;
spawn_flags = G_SPAWN_DEFAULT ;
if ( ! g_spawn_async ( NULL , cmd , NULL , spawn_flags , NULL , NULL , & pid , & error ) ) {
printf ( " Could not spawn:%s \n " , error - > message ) ;
}
printf ( " Spawned:%d. \n " , pid ) ;
2017-11-06 15:02:05 +03:00
}
static void
dispose ( GObject * object )
{
2017-11-06 15:08:27 +03:00
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( object ) ;
2017-11-06 15:02:05 +03:00
nm_clear_g_source ( & priv - > connect_timer ) ;
if ( priv - > pid ) {
pids_pending_send_sigterm ( priv - > pid ) ;
priv - > pid = 0 ;
}
2017-11-06 15:08:27 +03:00
G_OBJECT_CLASS ( nm_wireguard_plugin_parent_class ) - > dispose ( object ) ;
2017-11-06 15:02:05 +03:00
}
static void
2017-11-06 15:08:27 +03:00
nm_wireguard_plugin_class_init ( NMWireguardPluginClass * plugin_class )
2017-11-06 15:02:05 +03:00
{
GObjectClass * object_class = G_OBJECT_CLASS ( plugin_class ) ;
NMVpnServicePluginClass * parent_class = NM_VPN_SERVICE_PLUGIN_CLASS ( plugin_class ) ;
2017-11-06 15:08:27 +03:00
g_type_class_add_private ( object_class , sizeof ( NMWireguardPluginPrivate ) ) ;
2017-11-06 15:02:05 +03:00
object_class - > dispose = dispose ;
/* virtual methods */
2017-11-06 15:08:27 +03:00
// IMPLEMENT ME RIGHT
parent_class - > connect = test_connect ;
parent_class - > connect_interactive = test_connect_interactive ;
parent_class - > need_secrets = test_need_secrets ;
parent_class - > disconnect = test_disconnect ;
parent_class - > new_secrets = test_new_secrets ;
2017-11-06 15:02:05 +03:00
}
static void
2017-11-06 15:08:27 +03:00
plugin_state_changed ( NMWireguardPlugin * plugin ,
2017-11-06 15:02:05 +03:00
NMVpnServiceState state ,
gpointer user_data )
{
2017-11-06 15:08:27 +03:00
NMWireguardPluginPrivate * priv = NM_WIREGUARD_PLUGIN_GET_PRIVATE ( plugin ) ;
2017-11-06 15:02:05 +03:00
switch ( state ) {
case NM_VPN_SERVICE_STATE_UNKNOWN :
case NM_VPN_SERVICE_STATE_INIT :
case NM_VPN_SERVICE_STATE_SHUTDOWN :
case NM_VPN_SERVICE_STATE_STOPPING :
case NM_VPN_SERVICE_STATE_STOPPED :
/* Cleanup on failure */
nm_clear_g_source ( & priv - > connect_timer ) ;
nm_openvpn_disconnect_management_socket ( plugin ) ;
break ;
default :
break ;
}
}
2017-11-06 15:08:27 +03:00
NMWireguardPlugin *
nm_wireguard_plugin_new ( const char * bus_name )
2017-11-06 15:02:05 +03:00
{
2017-11-06 15:08:27 +03:00
NMWireguardPlugin * plugin ;
2017-11-06 15:02:05 +03:00
GError * error = NULL ;
2017-11-06 15:08:27 +03:00
// TODO rem
printf ( " %s \n " , NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME ) ;
printf ( " %s \n " , bus_name ) ;
// NOTE: owning this name must be allowed in a DBUS configuration file:
// "/etc/dbus-1/system.d/nm-wireguard-service.conf"
// (an example conf file was copied to the root of this project)
plugin = ( NMWireguardPlugin * ) g_initable_new ( NM_TYPE_WIREGUARD_PLUGIN , NULL , & error ,
2017-11-06 15:02:05 +03:00
NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME , bus_name ,
NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER , ! gl . debug ,
NULL ) ;
if ( plugin ) {
g_signal_connect ( G_OBJECT ( plugin ) , " state-changed " , G_CALLBACK ( plugin_state_changed ) , NULL ) ;
2017-11-06 15:08:27 +03:00
// FIXME export interface
/*
void * iface = NULL ;
void * conn = NULL ;
g_dbus_interface_skeleton_export ( iface , conn , NM_DBUS_PATH_OPENVPN , & error ) ;
*/
2017-11-06 15:02:05 +03:00
} else {
_LOGW ( " Failed to initialize a plugin instance: %s " , error - > message ) ;
g_error_free ( error ) ;
}
return plugin ;
}
static gboolean
signal_handler ( gpointer user_data )
{
g_main_loop_quit ( user_data ) ;
return G_SOURCE_REMOVE ;
}
static void
quit_mainloop ( NMVpnServicePlugin * plugin , gpointer user_data )
{
g_main_loop_quit ( ( GMainLoop * ) user_data ) ;
}
int
main ( int argc , char * argv [ ] )
{
2017-11-06 15:08:27 +03:00
NMWireguardPlugin * plugin ;
2017-11-06 15:02:05 +03:00
gboolean persist = FALSE ;
GOptionContext * opt_ctx = NULL ;
gchar * bus_name = NM_DBUS_SERVICE_OPENVPN ;
GError * error = NULL ;
GMainLoop * loop ;
GOptionEntry options [ ] = {
{ " persist " , 0 , 0 , G_OPTION_ARG_NONE , & persist , N_ ( " Don’ t quit when VPN connection terminates " ) , NULL } ,
{ " debug " , 0 , 0 , G_OPTION_ARG_NONE , & gl . debug , N_ ( " Enable verbose debug logging (may expose passwords) " ) , NULL } ,
{ " bus-name " , 0 , 0 , G_OPTION_ARG_STRING , & bus_name , N_ ( " D-Bus name to use for this instance " ) , NULL } ,
{ NULL }
} ;
# if !GLIB_CHECK_VERSION (2, 35, 0)
g_type_init ( ) ;
# endif
2017-11-06 15:08:27 +03:00
// TODO rem, was: "OPENVPN_DEBUG"
if ( getenv ( " WIREGUARD_DEBUG " ) )
2017-11-06 15:02:05 +03:00
gl . debug = TRUE ;
/* locale will be set according to environment LC_* variables */
setlocale ( LC_ALL , " " ) ;
2017-11-06 15:08:27 +03:00
bindtextdomain ( GETTEXT_PACKAGE , NM_WIREGUARD_LOCALEDIR ) ;
2017-11-06 15:02:05 +03:00
bind_textdomain_codeset ( GETTEXT_PACKAGE , " UTF-8 " ) ;
textdomain ( GETTEXT_PACKAGE ) ;
/* Parse options */
opt_ctx = g_option_context_new ( NULL ) ;
g_option_context_set_translation_domain ( opt_ctx , GETTEXT_PACKAGE ) ;
g_option_context_set_ignore_unknown_options ( opt_ctx , FALSE ) ;
g_option_context_set_help_enabled ( opt_ctx , TRUE ) ;
g_option_context_add_main_entries ( opt_ctx , options , NULL ) ;
2017-11-06 15:08:27 +03:00
// TODO translate
2017-11-06 15:02:05 +03:00
g_option_context_set_summary ( opt_ctx ,
_ ( " nm-openvpn-service provides integrated "
" OpenVPN capability to NetworkManager. " ) ) ;
if ( ! g_option_context_parse ( opt_ctx , & argc , & argv , & error ) ) {
g_printerr ( " Error parsing the command line options: %s \n " , error - > message ) ;
g_option_context_free ( opt_ctx ) ;
g_clear_error ( & error ) ;
exit ( 1 ) ;
}
g_option_context_free ( opt_ctx ) ;
gl . log_level = _nm_utils_ascii_str_to_int64 ( getenv ( " NM_VPN_LOG_LEVEL " ) ,
10 , 0 , LOG_DEBUG , - 1 ) ;
if ( gl . log_level > = 0 ) {
if ( gl . log_level > = LOG_DEBUG )
gl . log_level_ovpn = 10 ;
else if ( gl . log_level > = LOG_INFO )
gl . log_level_ovpn = 5 ;
else if ( gl . log_level > 0 )
gl . log_level_ovpn = 2 ;
else
gl . log_level_ovpn = 1 ;
} else if ( gl . debug )
gl . log_level_ovpn = 10 ;
else {
/* the default level is already "--verb 1", which is fine for us. */
gl . log_level_ovpn = - 1 ;
}
if ( gl . log_level < 0 )
gl . log_level = gl . debug ? LOG_INFO : LOG_NOTICE ;
gl . log_syslog = _nm_utils_ascii_str_to_int64 ( getenv ( " NM_VPN_LOG_SYSLOG " ) ,
10 , 0 , 1 ,
gl . debug ? 0 : 1 ) ;
2017-11-06 15:08:27 +03:00
_LOGD ( " nm-wireguard-service (version " DIST_VERSION " ) starting... " ) ;
2017-11-06 15:02:05 +03:00
2017-11-06 15:08:27 +03:00
// TODO what is this, rem
2017-11-06 15:02:05 +03:00
if ( ! g_file_test ( " /sys/class/misc/tun " , G_FILE_TEST_EXISTS )
2017-11-06 15:08:27 +03:00
& & ( system ( " /sbin/modprobe tun " ) = = - 1 ) ) {
printf ( " tun stuff not found :> \n " ) ;
exit ( EXIT_FAILURE ) ;
}
printf ( " tun stuff seems okay tho \n " ) ;
2017-11-06 15:02:05 +03:00
2017-11-06 15:08:27 +03:00
// TODO fails here:
// nm-openvpn[27808] <warn> Failed to initialize a plugin instance: Connection ":1.598" is not allowed to own the service "org.freedesktop.NetworkManager.openvpn" due to security policies in the configuration file
plugin = nm_wireguard_plugin_new ( bus_name ) ;
if ( ! plugin ) {
2017-11-06 15:02:05 +03:00
exit ( EXIT_FAILURE ) ;
2017-11-06 15:08:27 +03:00
}
2017-11-06 15:02:05 +03:00
loop = g_main_loop_new ( NULL , FALSE ) ;
if ( ! persist )
g_signal_connect ( plugin , " quit " , G_CALLBACK ( quit_mainloop ) , loop ) ;
signal ( SIGPIPE , SIG_IGN ) ;
g_unix_signal_add ( SIGTERM , signal_handler , loop ) ;
g_unix_signal_add ( SIGINT , signal_handler , loop ) ;
2017-11-06 15:08:27 +03:00
printf ( " Running the main loop ;> \n " ) ;
2017-11-06 15:02:05 +03:00
g_main_loop_run ( loop ) ;
g_object_unref ( plugin ) ;
pids_pending_wait_for_processes ( loop ) ;
g_main_loop_unref ( loop ) ;
2017-11-06 15:08:27 +03:00
printf ( " Exiting... \n " ) ;
2017-11-06 15:02:05 +03:00
exit ( EXIT_SUCCESS ) ;
}