Project

General

Profile

« Previous | Next » 

Revision ae472dc1

Added by Shawn Bruce over 5 years ago

OpenVPN radius ACL enhancements. Issue #9206

View differences:

src/etc/inc/openvpn.attributes.php
21 21
 * limitations under the License.
22 22
 */
23 23

  
24
global $username;
24
global $username, $dev, $untrusted_port;
25 25

  
26
$devname = getenv("dev");
27
if (empty($devname)) {
28
	$devname = "openvpn";
26
if (empty($dev)) {
27
	$dev = "openvpn";
29 28
}
30 29

  
31 30
function cisco_to_cidr($addr) {
32 31
	if (!is_ipaddr($addr)) {
33
		return 0;
32
		throw new Exception('Invalid IP Addr');
34 33
	}
34

  
35 35
	$mask = decbin(~ip2long($addr));
36 36
	$mask = substr($mask, -32);
37 37
	$k = 0;
......
42 42
}
43 43

  
44 44
function cisco_extract_index($prule) {
45

  
46 45
	$index = explode("#", $prule);
47 46
	if (is_numeric($index[1])) {
48 47
		return intval($index[1]);
......
52 51
	return -1;;
53 52
}
54 53

  
54
function parse_cisco_acl_rule($rule, $devname, $dir) {
55
	$rule_orig = $rule;
56
	$rule = explode(" ", $rule);
57
	$tmprule = "";
58
	$index = 0;
59

  
60
	if ($rule[$index] == "permit") {
61
		$tmprule = "pass {$dir} quick on {$devname} ";
62
	} else if ($rule[$index] == "deny") {
63
		$tmprule = "block {$dir} quick on {$devname} ";
64
	} else {
65
		return;
66
	}
67

  
68
	$index++;
69

  
70
	switch ($rule[$index]) {
71
		case "ip":
72
			$tmprule .= "inet ";
73
			break;
74
		case "icmp":
75
		case "tcp":
76
		case "udp":
77
			$tmprule .= "proto {$rule[$index]} ";
78
			break;
79
		default:
80
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid protocol.");
81
			return;
82
	}
83
	$index++;
84

  
85
	/* Source */
86
	if (trim($rule[$index]) == "host") {
87
		$index++;
88
		$tmprule .= "from {$rule[$index]} ";
89
		$index++;
90
	} else if (trim($rule[$index]) == "any") {
91
		$tmprule .= "from any ";
92
		$index++;
93
	} else {
94
		$network = $rule[$index];
95
		$netmask = $rule[++$index];
96

  
97

  
98
		if(!is_ipaddr($network)) {
99
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid source network '$network'.");
100
			return;
101
		}
102

  
103
		try {
104
			$netmask = cisco_to_cidr($netmask);
105
		} catch(Exception $e) {
106
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid source netmask '$netmask'.");
107
			return;
108
		}
109
		$tmprule .= "from {network}/{$netmask}";
110

  
111
		$index++;
112
	}
113

  
114
	/* Source Operator */
115
	if (in_array(trim($rule[$index]), array("lt", "gt", "eq", "neq"))) {
116
		switch(trim($rule[$index])) {
117
			case "lt":
118
				$operator = "<";
119
				break;
120
			case "gt":
121
				$operator = ">";
122
				break;
123
			case "eq":
124
				$operator = "=";
125
				break;
126
			case "neq":
127
				$operator = "!=";
128
				break;
129
		}
130

  
131
		$port = $rule[++$index];
132
		if (is_port($port)) {
133
			$tmprule .= "port {$operator} {$port} ";
134
		} else {
135
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid source port: '$port' not a numeric value between 0 and 65535.");
136
			return;
137
		}
138
		$index++;
139
	} else if (trim($rule[$index]) == "range") {
140
		$port = array($rule[++$index], $rule[++$index]);
141
		if (is_port($port[0]) && is_port($port[1])) {
142
			$port[0]--;
143
			$port[1]++;
144
			$tmprule .= "port {$port[0]} >< {$port[1]} ";
145
		} else {
146
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid source ports: '$port[0]' & '$port[1]' one or both are not a numeric value between 0 and 65535.");
147
			return;
148
		}
149
		$index++;
150
	}
151

  
152
	/* Destination */
153
	if (trim($rule[$index]) == "host") {
154
		$index++;
155
		$tmprule .= "to {$rule[$index]} ";
156
		$index++;
157
	} else if (trim($rule[$index]) == "any") {
158
		$tmprule .= "to any ";
159
		$index++;
160
	} else {
161
		$network = $rule[$index];
162
		$netmask = $rule[++$index];
163

  
164

  
165
		if(!is_ipaddr($network)) {
166
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid destination network '$network'.");
167
			return;
168
		}
169

  
170
		try {
171
			$netmask = cisco_to_cidr($netmask);
172
		} catch(Exception $e) {
173
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid destination netmask '$netmask'.");
174
			return;
175
		}
176
		$tmprule .= "to {network}/{$netmask}";
177

  
178
		$index++;
179
	}
180

  
181
	/* Destination Operator */
182
	if (in_array(trim($rule[$index]), array("lt", "gt", "eq", "neq"))) {
183
		switch(trim($rule[$index])) {
184
			case "lt":
185
				$operator = "<";
186
				break;
187
			case "gt":
188
				$operator = ">";
189
				break;
190
			case "eq":
191
				$operator = "=";
192
				break;
193
			case "neq":
194
				$operator = "!=";
195
				break;
196
		}
197

  
198
		$port = $rule[++$index];
199
		if (is_port($port)) {
200
			$tmprule .= "port {$operator} {$port} ";
201
		} else {
202
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid destination port: '$port' not a numeric value between 0 and 65535.");
203
			return;
204
		}
205
		$index++;
206
	} else if (trim($rule[$index]) == "range") {
207
		$port = array($rule[++$index], $rule[++$index]);
208
		if (is_port($port[0]) && is_port($port[1])) {
209
			$port[0]--;
210
			$port[1]++;
211
			$tmprule .= "port {$port[0]} >< {$port[1]} ";
212
		} else {
213
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid destination ports: '$port[0]' '$port[1]' one or both are not a numeric value between 0 and 65535.");
214
			return;
215
		}
216
		$index++;
217
	}
218

  
219
	return $tmprule;
220
}
221

  
55 222
function parse_cisco_acl($attribs) {
56
	global $devname, $attributes;
223
	global $dev, $attributes;
57 224
	if (!is_array($attribs)) {
58 225
		return "";
59 226
	}
......
83 250
				continue;
84 251
			}
85 252

  
86
			$rule = $rule[1];
87
			$rule = explode(" ", $rule);
88
			$tmprule = "";
89
			$index = 0;
90
			$isblock = false;
91
			if ($rule[$index] == "permit") {
92
				$tmprule = "pass {$dir} quick on {$devname} ";
93
			} else if ($rule[$index] == "deny") {
94
				//continue;
95
				$isblock = true;
96
				$tmprule = "block {$dir} quick on {$devname} ";
97
			} else {
98
				continue;
99
			}
100

  
101
			$index++;
102

  
103
			switch ($rule[$index]) {
104
				case "tcp":
105
				case "udp":
106
					$tmprule .= "proto {$rule[$index]} ";
107
					break;
108
			}
109

  
110
			$index++;
111
			/* Source */
112
			if (trim($rule[$index]) == "host") {
113
				$index++;
114
				$tmprule .= "from {$rule[$index]} ";
115
				$index++;
116
				if ($isblock == true) {
117
					$isblock = false;
118
				}
119
			} else if (trim($rule[$index]) == "any") {
120
				$tmprule .= "from any ";
121
				$index++;
122
			} else {
123
				$tmprule .= "from {$rule[$index]}";
124
				$index++;
125
				$netmask = cisco_to_cidr($rule[$index]);
126
				$tmprule .= "/{$netmask} ";
127
				$index++;
128
				if ($isblock == true) {
129
					$isblock = false;
130
				}
131
			}
132
			/* Destination */
133
			if (trim($rule[$index]) == "host") {
134
				$index++;
135
				$tmprule .= "to {$rule[$index]} ";
136
				$index++;
137
				if ($isblock == true) {
138
					$isblock = false;
139
				}
140
			} else if (trim($rule[$index]) == "any") {
141
				$index++;
142
				$tmprule .= "to any";
143
			} else {
144
				$tmprule .= "to {$rule[$index]}";
145
				$index++;
146
				$netmask = cisco_to_cidr($rule[$index]);
147
				$tmprule .= "/{$netmask} ";
148
				$index++;
149
				if ($isblock == true) {
150
					$isblock = false;
151
				}
152
			}
153

  
154
			if ($isblock == true) {
155
				continue;
156
			}
253
			$tmprule = parse_cisco_acl_rule($rule[1], $dev, $dir);
157 254

  
158 255
			if ($dir == "in") {
159 256
				$inrules[$rindex] = $tmprule;
......
183 280

  
184 281
$rules = parse_cisco_acl($attributes);
185 282
if (!empty($rules)) {
186
	$pid = posix_getpid();
187
	$filename = "{$g['tmp_path']}/ovpn_{$pid}{$username}.rules";
283
	$filename = "{$g['tmp_path']}/ovpn_{$dev}_{$username}_{$untrusted_port}.rules";
188 284
	@file_put_contents($filename, $rules);
189
	mwexec("/sbin/pfctl -a " . escapeshellarg("openvpn/{$username}") . " -f " . escapeshellarg($filename));
190
	@unlink($filename);
191 285
}
192 286

  
193 287
?>
src/etc/inc/openvpn.auth-user.php
36 36
/* setup syslog logging */
37 37
openlog("openvpn", LOG_ODELAY, LOG_AUTH);
38 38

  
39
global $common_name, $username;
39
global $common_name, $username, $dev, $untrusted_port;
40 40

  
41 41
if (isset($_GET['username'])) {
42 42
	$authmodes = explode(",", base64_decode($_GET['authcfg']));
......
46 46
	$common_name = $_GET['cn'];
47 47
	$modeid = $_GET['modeid'];
48 48
	$strictusercn = $_GET['strictcn'] == "false" ? false : true;
49
	$dev = $_GET['dev'];
50
	$untrusted_port = $_GET['untrusted_port'];
49 51
} else {
50 52
	/* read data from environment */
51 53
	$username = getenv("username");
52 54
	$password = getenv("password");
53 55
	$common_name = getenv("common_name");
56
	$dev = getenv("dev");
57
	$untrusted_port = getenv("untrusted_port");
54 58
}
55 59

  
56 60
if (!$username) {
src/usr/local/sbin/openvpn.attributes.sh
20 20
# See the License for the specific language governing permissions and
21 21
# limitations under the License.
22 22

  
23

  
24
lockfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.lock"
25
rulesfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.rules"
26
anchorname="openvpn/${dev}_${username}_${trusted_port}"
27

  
23 28
if [ "$script_type" = "client-connect" ]; then
29
	i=1
30
	while [ -f "${lockfile}" ]; do
31
		if [ $i -ge 30 ]; then
32
			/bin/echo "Timeout while waiting for lockfile"
33
			exit 1
34
		fi
35

  
36
		/bin/sleep 1
37
		i=$(( i + 1 ))
38
	done
39
	/usr/bin/touch "${lockfile}"
40

  
41
	/bin/cat "${rulesfile}" | /usr/bin/sed "s/{clientip}/${ifconfig_pool_remote_ip}/g" > "${rulesfile}.tmp" && /bin/mv "${rulesfile}.tmp" "${rulesfile}"
42
	/sbin/pfctl -a "openvpn/${dev}_${username}_${trusted_port}" -f "${rulesfile}"
43
	/bin/rm "${rulesfile}"
44

  
24 45
	if [ -f /tmp/$common_name ]; then
25 46
		/bin/cat /tmp/$common_name > $1
26 47
		/bin/rm /tmp/$common_name
27 48
	fi
49

  
50
	/bin/rm "${lockfile}"
28 51
elif [ "$script_type" = "client-disconnect" ]; then
29
	command="/sbin/pfctl -a 'openvpn/$common_name' -F rules"
52
	i=1
53
	while [ -f "${lockfile}" ]; do
54
		if [ $i -ge 30 ]; then
55
			/bin/echo "Timeout while waiting for lockfile"
56
			exit 1
57
		fi
58

  
59
		/bin/sleep 1
60
		i=$(( i + 1 ))
61
	done
62
	/usr/bin/touch "${lockfile}"
63

  
64
	command="/sbin/pfctl -a '${anchorname}' -F rules"
30 65
	eval $command
31 66
	/sbin/pfctl -k $ifconfig_pool_remote_ip
32 67
	/sbin/pfctl -K $ifconfig_pool_remote_ip
68

  
69
	/bin/rm "${lockfile}"
33 70
fi
34 71

  
35 72
exit 0
src/usr/local/sbin/ovpn_auth_verify_async
62 62
# ---------- Perform Check
63 63
auth_credentials="username=${username}&password=${password}"
64 64
# Note that common_name is also assumed to be set by the environment
65
auth_server_1="cn=${common_name}&strictcn=${strictcn}&authcfg=${authcfg}"
65
auth_server_1="cn=${common_name}&strictcn=${strictcn}&authcfg=${authcfg}&dev=${dev}&untrusted_port=${untrusted_port}"
66 66
auth_server_2="modeid=${modeid}&nas_port=${nas_port}"
67 67
auth_args="${auth_credentials}&${auth_server_1}&${auth_server_2}"
68 68

  
......
72 72

  
73 73
auth_result="${auth_failure}"
74 74
if [ "${result}" = "OK" ]; then
75
    auth_result="${auth_success}"
75
	auth_result="${auth_success}"
76 76
fi
77 77

  
78 78
# The output file path should be set in the environment

Also available in: Unified diff