aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/installer/customactions.js
blob: c35774adf1f94e2851078da07a329544fab57bde (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/* SPDX-License-Identifier: MIT
 *
 * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
 */

var wsh = new ActiveXObject("WScript.Shell");
var shl = new ActiveXObject("Shell.Application");
var fso = new ActiveXObject("Scripting.FileSystemObject");

function logMessage(msg) {
	var record = Installer.CreateRecord(1);
	record.StringData(0) = "Custom action: [1]";
	record.StringData(1) = msg.toString();
	Session.Message(0x04000000, record);
}

// I'd rather use wsh.Exec, so that we can just do ".Stdout.ReadAll()", but
// this results in a scary flashing command window. The below is the best
// workaround we can find yet. We'll keep searching for more clever tricks.
function runWithNoWindowFlash(command) {
	//TODO: Seems pretty unlikely that this temp file is secure...
	var tmpfile = fso.BuildPath(fso.GetSpecialFolder(2), fso.GetTempName());
	try {
		//TODO: Obviously cmd and tmpfile are unescaped here...
		var cmd = fso.BuildPath(fso.GetSpecialFolder(1), "cmd.exe") + " /c " + command + " > \"" + tmpfile + "\"";
		var ret = wsh.Run(cmd, 0, true);
		if (ret != 0) {
			logMessage("Command " + cmd + " exited with error " + ret.toString());
			return "";
		}
		var txt;
		try {
			var file = fso.OpenTextFile(tmpfile, 1);
			txt = file.ReadAll();
			file.Close();
		} catch (e) {
			logMessage("Unable to read temporary file " + tmpfile + " for command " + cmd + ": " + e.message);
			return "";
		}
		return txt;
	} finally {
		try {
			fso.DeleteFile(tmpfile);
		} catch (e) {}
	}
}

function EvaluateWireGuardServices() {
	var inst = Session.Installer;
	var db = Session.Database;
	var view = db.OpenView("INSERT INTO `ServiceControl` (`ServiceControl`, `Name`, `Event`, `Component_`, `Wait`) VALUES(?, ?, ?, ?, ?) TEMPORARY");
	var rec = inst.CreateRecord(5);
	var serviceKey = "HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Services";
	var servicePrefix = "WireGuardTunnel$";
	var serviceKeyPrefix = serviceKey + "\\" + servicePrefix;
	var allowedNameFormat = new RegExp("^[a-zA-Z0-9_=+.-]{1,32}$");
	var msiOperators = new RegExp("[=+-]", "g");
	var index = 0;

	function insertServiceControl(serviceName) {
		logMessage("Scheduling stop on upgrade or removal on uninstall of service " + serviceName);
		rec.StringData (1/*ServiceControl*/) = "stop_" + serviceName.replace(msiOperators, "_") + (index++).toString();
		rec.StringData (2/*Name          */) = serviceName;
		rec.IntegerData(3/*Event         */) = 0x2/*msidbServiceControlEventStop*/ | 0x20/*msidbServiceControlEventUninstallStop*/ | 0x80/*msidbServiceControlEventUninstallDelete*/;
		rec.StringData (4/*Component_    */) = "WireGuardExecutable";
		rec.IntegerData(5/*Wait          */) = 1; /* Waits 30 seconds. */
		view.Execute(rec);
		
		if (!shl.IsServiceRunning(serviceName))
			return;
		
		logMessage("Scheduling start on upgrade of service " + serviceName);
		rec.StringData (1/*ServiceControl*/) = "start_" + serviceName.replace(msiOperators, "_") + (index++).toString();
		rec.StringData (2/*Name          */) = serviceName;
		rec.IntegerData(3/*Event         */) = 0x1/*msidbServiceControlEventStart*/;
		rec.StringData (4/*Component_    */) = "WireGuardExecutable";
		rec.IntegerData(5/*Wait          */) = 0; /* No wait, so that failure to restart again isn't fatal. */
		view.Execute(rec);
	}

	insertServiceControl("WireGuardManager");

	var txt = runWithNoWindowFlash(fso.BuildPath(fso.GetSpecialFolder(1), "reg.exe") + " query \"" + serviceKey + "\"");
	var lines = txt.split(new RegExp("\r?\n", "g"));
	for (var i = 0; i < lines.length; ++i) {
		if (lines[i].length > serviceKeyPrefix.length && lines[i].substring(0, serviceKeyPrefix.length) == serviceKeyPrefix) {
			var tunnelName = lines[i].substring(serviceKeyPrefix.length);
			if (tunnelName.match(allowedNameFormat) != null)
				insertServiceControl(servicePrefix + tunnelName);
		}
	}
}

function RemoveConfigFolder() {
	try {
		var path = wsh.RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\S-1-5-18\\ProfileImagePath");
		path = wsh.ExpandEnvironmentStrings(path.toString());
		fso.DeleteFolder(fso.BuildPath(path, "AppData\\Local\\WireGuard"), true);
	} catch(e) {
		logMessage("Failed to remove configuration on uninstall: " + e.message);
	}
}