mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-06-05 06:38:18 +02:00
We no longer add these. If you use Emacs, configure it yourself.
Also, due to our "smart-tab" usage the editor anyway does a subpar
job handling our tabs. However, on the upside every user can choose
whatever tab-width he/she prefers. If "smart-tabs" are used properly
(like we do), every tab-width will work.
No manual changes, just ran commands:
F=($(git grep -l -e '-\*-'))
sed '1 { /\/\* *-\*- *[mM]ode.*\*\/$/d }' -i "${F[@]}"
sed '1,4 { /^\(#\|--\|dnl\) *-\*- [mM]ode/d }' -i "${F[@]}"
Check remaining lines with:
git grep -e '-\*-'
The ultimate purpose of this is to cleanup our files and eventually use
SPDX license identifiers. For that, first get rid of the boilerplate lines.
431 lines
15 KiB
Lua
Executable file
431 lines
15 KiB
Lua
Executable file
#!/usr/bin/env lua
|
|
-- vim: ft=lua ts=2 sts=2 sw=2 et ai
|
|
--
|
|
-- 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.
|
|
--
|
|
-- Copyright 2015 Red Hat, Inc.
|
|
--
|
|
--
|
|
-- Script for importing/converting Cisco VPN configuration files (.pcf) to NetworkManager
|
|
-- In general, the implementation follows the logic of import() from
|
|
-- https://git.gnome.org/browse/network-manager-vpnc/tree/properties/nm-vpnc.c
|
|
--
|
|
|
|
----------------------
|
|
-- Helper functions --
|
|
----------------------
|
|
function read_all(in_file)
|
|
local f, msg = io.open(in_file, "r")
|
|
if not f then return nil, msg; end
|
|
local content = f:read("*all")
|
|
f:close()
|
|
return content
|
|
end
|
|
|
|
function uuid()
|
|
math.randomseed(os.time())
|
|
local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
|
local uuid = string.gsub(template, '[xy]', function (c)
|
|
local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
|
|
return string.format('%x', v)
|
|
end)
|
|
return uuid
|
|
end
|
|
|
|
function vpn_settings_to_text(vpn_settings)
|
|
local t = {}
|
|
for k,v in pairs(vpn_settings) do
|
|
t[#t+1] = k.."="..v
|
|
end
|
|
return table.concat(t, "\n")
|
|
end
|
|
|
|
function usage()
|
|
local basename = string.match(arg[0], '[^/\\]+$') or arg[0]
|
|
print(basename .. " - convert/import Cisco VPN (.pcf) configuration to NetworkManager")
|
|
print("Usage:")
|
|
print(" " .. basename .. " <input-file> <output-file>")
|
|
print(" - converts Cisco VPN config to NetworkManager keyfile")
|
|
print("")
|
|
print(" " .. basename .. " --import <input-file1> <input-file2> ...")
|
|
print(" - imports Cisco VPN config(s) to NetworkManager")
|
|
os.exit(1)
|
|
end
|
|
|
|
|
|
-------------------------------------------
|
|
-- Functions for VPN options translation --
|
|
-------------------------------------------
|
|
function set_option(t, option, value)
|
|
g_switches[value[1]] = value[2]
|
|
end
|
|
function handle_generic(t, option, value)
|
|
t[option] = value[2]
|
|
end
|
|
function handle_yes(t, option, value)
|
|
t[option] = "yes"
|
|
end
|
|
function handle_bool(t, option, value)
|
|
if tonumber(value[2]) == 1 then
|
|
t[option] = "true"
|
|
elseif tonumber(value[2]) == 0 then
|
|
t[option] = "false"
|
|
else
|
|
io.stderr:write(string.format("Warning: ignoring invalid option '%s'\n", value[1]))
|
|
end
|
|
end
|
|
function handle_DHGroup(t, option, value)
|
|
local dhgroups = { [1]="dh1", [2]="dh2", [5]="dh5" }
|
|
dhgroup = dhgroups[tonumber(value[2])]
|
|
if not dhgroup then io.stderr:write(string.format("Warning: invalid value for 'DHGroup': %s\n", value[2])) end
|
|
t[option] = dhgroup
|
|
end
|
|
function handle_PeerTimeout(t, option, value)
|
|
if not value[2] then io.stderr:write("Warning: ignoring invalid option 'PeerTimeout'\n") end
|
|
if tonumber(value[2]) == 0 or (tonumber(value[2]) >=10 and tonumber(value[2] <= 86400)) then
|
|
t[option] = value[2]
|
|
else io.stderr:write(string.format("Warning: invalid value for 'PeerTimeout': %s\n", value[2])) end
|
|
end
|
|
function handle_(t, option, value)
|
|
io.stderr:write("Warning: enc_GroupPwd: encrypted group passwords are not supported by this script.\n")
|
|
end
|
|
function handle_TunnelingMode(t, option, value)
|
|
if value[2] == 1 then
|
|
io.stderr:write("Warning: TCP tunneling is not supported by vpnc. " ..
|
|
"The connection will be used with TCP tunneling disabled, " ..
|
|
"however it may not work as expected.\n")
|
|
end
|
|
end
|
|
function handle_UseLegacyIKEPort(t, option, value)
|
|
if value[2] ~= 0 then
|
|
t[option] = 500
|
|
end
|
|
end
|
|
function handle_routes(t, option, value)
|
|
local function splitroutes(str)
|
|
local sep, fields = " ", {}
|
|
local pattern = string.format("([^%s]+)", sep)
|
|
str:gsub(pattern,
|
|
function(c)
|
|
local c1,c2 = c:match("^(%d+%.%d+%.%d+%.%d+)/(%d+)$")
|
|
if c1 then
|
|
fields[#fields+1] = { c1, c2 }
|
|
else
|
|
io.stderr:write("Warning: ignoring invalid route: '" .. c .. "'\n")
|
|
end
|
|
end)
|
|
return fields
|
|
end
|
|
t[option] = splitroutes(value[2])
|
|
end
|
|
|
|
-- global variables -
|
|
g_vpn_data = {}
|
|
g_vpn_pwds = {}
|
|
g_con_data = {}
|
|
g_ip4_data = {}
|
|
g_switches = {}
|
|
|
|
vpn2nm = {
|
|
["Description"] = { nm_opt="id", func=handle_generic, tbl=g_con_data },
|
|
["InterfaceName"] = { nm_opt="interface-name", func=handle_generic, tbl=g_con_data },
|
|
["EnableLocalLAN"] = { nm_opt="never-default", func=handle_bool, tbl=g_ip4_data },
|
|
["X-NM-Routes"] = { nm_opt="routes", func=handle_routes, tbl=g_ip4_data },
|
|
["Host"] = { nm_opt="IPSec gateway", func=handle_generic, tbl=g_vpn_data },
|
|
["GroupName"] = { nm_opt="IPSec ID", func=handle_generic, tbl=g_vpn_data },
|
|
["Username"] = { nm_opt="Xauth username", func=handle_generic, tbl=g_vpn_data },
|
|
["UserPassword"] = { nm_opt="Xauth password", func=handle_generic, tbl=g_vpn_pwds },
|
|
["SaveUserPassword"] = { nm_opt="", func=set_option, tbl={} },
|
|
["GroupPwd"] = { nm_opt="IPSec secret", func=handle_generic, tbl=g_vpn_pwds },
|
|
["DHGroup"] = { nm_opt="IKE DH Group", func=handle_DHGroup, tbl=g_vpn_data },
|
|
["NTDomain"] = { nm_opt="Domain", func=handle_generic, tbl=g_vpn_data },
|
|
["SingleDES"] = { nm_opt="Enable Single DES", func=handle_yes, tbl=g_vpn_data },
|
|
["EnableNat"] = { nm_opt="", func=set_option, tbl={} },
|
|
["X-NM-Use-NAT-T"] = { nm_opt="", func=set_option, tbl={} },
|
|
["X-NM-Force-NAT-T"] = { nm_opt="", func=set_option, tbl={} },
|
|
["X-NM-SaveGroupPassword"] = { nm_opt="", func=set_option, tbl={} },
|
|
["UseLegacyIKEPort"] = { nm_opt="Local Port", func=handle_UseLegacyIKEPort, tbl=g_vpn_data },
|
|
["PeerTimeout"] = { nm_opt="DPD idle timeout (our side)", func=handle_PeerTimeout, tbl=g_vpn_data },
|
|
["TunnelingMode"] = { nm_opt="", func=handle_TunnelingMode, tbl= {} },
|
|
["enc_UserPassword"] = { nm_opt="", func=handle_enc_pwd, tbl= {} },
|
|
["enc_GroupPwd"] = { nm_opt="", func=handle_enc_pwd, tbl= {} },
|
|
}
|
|
|
|
------------------------------------------------------
|
|
-- Read and convert the config into the global vars --
|
|
------------------------------------------------------
|
|
function read_and_convert(in_file)
|
|
local function line_split(str)
|
|
-- split at '=' character
|
|
local sep, fields = "=", {}
|
|
local pattern = string.format("([^%s]+)%s(.+)", sep, sep)
|
|
fields[1], fields[2] = str:match(pattern)
|
|
return fields
|
|
end
|
|
|
|
in_text, msg = read_all(in_file)
|
|
if not in_text then return false, msg end
|
|
|
|
-- loop through the config and convert it
|
|
for line in in_text:gmatch("[^\r\n]+") do
|
|
repeat
|
|
-- skip comments and empty lines
|
|
if line:find("^%s*[#;]") or line:find("^%s*$") then break end
|
|
-- trim leading and trailing spaces
|
|
line = line:find("^%s*$") and "" or line:match("^%s*(.*%S)")
|
|
|
|
local words = line_split(line)
|
|
local val = vpn2nm[words[1]]
|
|
if val then
|
|
if type(val) == "table" then val.func(val.tbl, val.nm_opt, words)
|
|
else print(string.format("debug: '%s': val=%s", line, val)) end
|
|
end
|
|
until true
|
|
end
|
|
|
|
-- check if mandatory options exist
|
|
if not g_vpn_data["IPSec gateway"] then
|
|
local msg = in_file .. ": Not a valid Cisco VPN configuration (no Host)"
|
|
return false, msg
|
|
end
|
|
if not g_vpn_data["IPSec ID"] then
|
|
local msg = in_file .. ": Not a valid OpenVPN configuration (no GroupName)"
|
|
return false, msg
|
|
end
|
|
|
|
-- process inter-option dependencies
|
|
-- NAT traversal mode
|
|
local natt_mode = {
|
|
NONE = "none",
|
|
NATT = "natt",
|
|
NATT_ALWAYS = "force-natt",
|
|
CISCO = "cisco-udp"
|
|
}
|
|
g_vpn_data["NAT Traversal Mode"] = natt_mode.CISCO
|
|
if tonumber(g_switches["EnableNat"]) == 0 then
|
|
g_vpn_data["NAT Traversal Mode"] = natt_mode.NONE
|
|
elseif tonumber(g_switches["EnableNat"]) == 1 then
|
|
if tonumber(g_switches["X-NM-Force-NAT-T"]) == 1 then
|
|
g_vpn_data["NAT Traversal Mode"] = natt_mode.NATT_ALWAYS
|
|
elseif tonumber(g_switches["X-NM-Use-NAT-T"]) == 1 then
|
|
g_vpn_data["NAT Traversal Mode"] = natt_mode.NATT
|
|
end
|
|
else
|
|
io.stderr:write("Warning: invalid value for EnableNat\n")
|
|
g_vpn_data["NAT Traversal Mode"] = natt_mode.CISCO
|
|
end
|
|
|
|
-- set secret flags
|
|
g_vpn_data["Xauth password-flags"] = 1
|
|
if tonumber(g_switches["SaveUserPassword"]) == 1 then
|
|
g_vpn_data["xauth-password-type"] = "save"
|
|
else
|
|
g_vpn_data["Xauth password-flags"] = 3
|
|
end
|
|
if g_vpn_data["IPSec ID"] then
|
|
g_vpn_data["IPSec ID-flags"] = 1
|
|
end
|
|
if g_switches["X-NM-SaveGroupPassword"] then
|
|
if tonumber(g_switches["X-NM-SaveGroupPassword"]) == 1 then
|
|
g_vpn_data["ipsec-secret-type"] = "save"
|
|
g_vpn_data["IPSec ID-flags"] = 1
|
|
else
|
|
g_vpn_data["IPSec ID-flags"] = 3
|
|
end
|
|
else
|
|
g_vpn_data["ipsec-secret-type"] = "save"
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
--------------------------------------------------------
|
|
-- Create and write connection file in keyfile format --
|
|
--------------------------------------------------------
|
|
function write_vpn_to_keyfile(in_file, out_file)
|
|
connection = [[
|
|
[connection]
|
|
id=__NAME_PLACEHOLDER__
|
|
uuid=__UUID_PLACEHOLDER__
|
|
__IFNAME_PLACEHOLDER__
|
|
type=vpn
|
|
autoconnect=no
|
|
|
|
[ipv4]
|
|
method=auto
|
|
never-default=__NEVER_DEFAULT_PLACEHOLDER__
|
|
__ROUTES_PLACEHOLDER__
|
|
|
|
[ipv6]
|
|
method=auto
|
|
|
|
[vpn]
|
|
service-type=org.freedesktop.NetworkManager.vpnc
|
|
]]
|
|
connection = connection .. vpn_settings_to_text(g_vpn_data)
|
|
connection = connection .. "\n\n[vpn-secrets]\n"
|
|
connection = connection .. vpn_settings_to_text(g_vpn_pwds)
|
|
|
|
local con_name = g_con_data["id"] or (out_file:gsub(".*/", ""))
|
|
local ifname = g_con_data["interface-name"]
|
|
local never_default = g_ip4_data["never-default"] or "false"
|
|
local routes = ""
|
|
if ifname then ifname = "interface-name="..ifname.."\n" else ifname = "" end
|
|
for idx, r in ipairs(g_ip4_data["routes"] or {}) do
|
|
routes = routes .. string.format("routes%d=%s/%s\n", idx, r[1], r[2])
|
|
end
|
|
|
|
connection = string.gsub(connection, "__NAME_PLACEHOLDER__", con_name)
|
|
connection = string.gsub(connection, "__UUID_PLACEHOLDER__", uuid())
|
|
connection = string.gsub(connection, "__IFNAME_PLACEHOLDER__\n", ifname)
|
|
connection = string.gsub(connection, "__NEVER_DEFAULT_PLACEHOLDER__", never_default)
|
|
connection = string.gsub(connection, "__ROUTES_PLACEHOLDER__\n", routes)
|
|
|
|
-- write output file
|
|
local f, err = io.open(out_file, "w")
|
|
if not f then io.stderr:write(err) return false end
|
|
f:write(connection)
|
|
f:close()
|
|
|
|
local ofname = out_file:gsub(".*/", "")
|
|
io.stderr:write("Successfully converted VPN configuration: " .. in_file .. " => " .. out_file .. "\n")
|
|
io.stderr:write("To use the connection, do:\n")
|
|
io.stderr:write("# cp " .. out_file .. " /etc/NetworkManager/system-connections\n")
|
|
io.stderr:write("# chmod 600 /etc/NetworkManager/system-connections/" .. ofname .. "\n")
|
|
io.stderr:write("# nmcli con load /etc/NetworkManager/system-connections/" .. ofname .. "\n")
|
|
return true
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- Import VPN connection to NetworkManager --
|
|
---------------------------------------------
|
|
function import_vpn_to_NM(filename)
|
|
local lgi = require 'lgi'
|
|
local GLib = lgi.GLib
|
|
local NM = lgi.NM
|
|
|
|
-- function creating NMConnection
|
|
local function create_profile(name)
|
|
local profile = NM.SimpleConnection.new()
|
|
local never_default = g_ip4_data["never-default"] == "true"
|
|
|
|
s_con = NM.SettingConnection.new()
|
|
s_vpn = NM.SettingVpn.new()
|
|
s_ip4 = NM.SettingIP4Config.new()
|
|
|
|
s_con[NM.SETTING_CONNECTION_ID] = name
|
|
s_con[NM.SETTING_CONNECTION_UUID] = uuid()
|
|
s_con[NM.SETTING_CONNECTION_INTERFACE_NAME] = g_con_data["interface-name"]
|
|
s_con[NM.SETTING_CONNECTION_TYPE] = "vpn"
|
|
s_vpn[NM.SETTING_VPN_SERVICE_TYPE] = "org.freedesktop.NetworkManager.vpnc"
|
|
s_ip4[NM.SETTING_IP_CONFIG_METHOD] = NM.SETTING_IP4_CONFIG_METHOD_AUTO
|
|
s_ip4[NM.SETTING_IP_CONFIG_NEVER_DEFAULT] = never_default
|
|
|
|
-- add routes
|
|
local AF_INET = 2
|
|
for _, r in ipairs(g_ip4_data["routes"] or {}) do
|
|
route = NM.IPRoute.new(AF_INET, r[1], r[2], nil, -1)
|
|
s_ip4:add_route(route)
|
|
end
|
|
|
|
-- add vpn data
|
|
for k,v in pairs(g_vpn_data) do
|
|
s_vpn:add_data_item(k, v)
|
|
end
|
|
-- add vpn secrets
|
|
for k,v in pairs(g_vpn_pwds) do
|
|
s_vpn:add_secret(k, v)
|
|
end
|
|
|
|
profile:add_setting(s_con)
|
|
profile:add_setting(s_vpn)
|
|
profile:add_setting(s_ip4)
|
|
return profile
|
|
end
|
|
|
|
-- callback function for add_connection()
|
|
local function added_cb(client, result, data)
|
|
local con,err,code = client:add_connection_finish(result)
|
|
if con then
|
|
print(string.format("%s: Imported to NetworkManager: %s - %s",
|
|
filename, con:get_uuid(), con:get_id()))
|
|
else
|
|
io.stderr:write(code .. ": " .. err .. "\n");
|
|
return false
|
|
end
|
|
main_loop:quit()
|
|
end
|
|
|
|
local profile_name = g_con_data["id"] or string.match(filename, '[^/\\]+$') or filename
|
|
main_loop = GLib.MainLoop(nil, false)
|
|
local con = create_profile(profile_name)
|
|
local client = NM.Client.new()
|
|
|
|
-- send the connection to NetworkManager
|
|
client:add_connection_async(con, true, nil, added_cb, nil)
|
|
|
|
-- run main loop so that the callback could be called
|
|
main_loop:run()
|
|
return true
|
|
end
|
|
|
|
|
|
---------------------------
|
|
-- Main code starts here --
|
|
---------------------------
|
|
local import_mode = false
|
|
local infile, outfile
|
|
|
|
-- parse command-line arguments
|
|
if not arg[1] or arg[1] == "--help" or arg[1] == "-h" then usage() end
|
|
if arg[1] == "--import" or arg[1] == "-i" then
|
|
infile = arg[2]
|
|
if not infile then usage() end
|
|
import_mode = true
|
|
else
|
|
infile = arg[1]
|
|
outfile = arg[2]
|
|
if not infile or not outfile then usage() end
|
|
if arg[3] then usage() end
|
|
end
|
|
|
|
if import_mode then
|
|
-- check if lgi is available
|
|
local success,msg = pcall(require, 'lgi')
|
|
if not success then
|
|
io.stderr:write("Lua lgi module is not available, please install it (usually lua-lgi package)\n")
|
|
-- print(msg)
|
|
os.exit(1)
|
|
end
|
|
-- read configs, convert them and import to NM
|
|
for i = 2, #arg do
|
|
ok, err_msg = read_and_convert(arg[i])
|
|
if ok then import_vpn_to_NM(arg[i])
|
|
else io.stderr:write(err_msg .. "\n") end
|
|
-- reset global vars
|
|
g_vpn_data = {}
|
|
g_vpn_pwds = {}
|
|
g_con_data = {}
|
|
g_ip4_data = {}
|
|
g_switches = {}
|
|
end
|
|
else
|
|
-- read configs, convert them and write as NM keyfile connection
|
|
ok, err_msg = read_and_convert(infile)
|
|
if ok then write_vpn_to_keyfile(infile, outfile)
|
|
else io.stderr:write(err_msg .. "\n") end
|
|
end
|
|
|