` elements if you want the forms as a whole to take on default CloudTrax styling.
49 |
50 | It's important to note that it's up to you to ascertain that the login form being shown on a splash page matches the type of authentication service you've configured.
51 |
52 | #### Free access (no authentication required)
53 |
54 | ````
55 |
Free Access:
56 |
57 |
62 | ````
63 |
64 | #### Voucher passcode entry
65 |
66 | ````
67 |
Passcode Access:
68 |
69 | Enter your access code below:
70 |
71 |
72 |
73 |
74 | $error_msg
75 |
76 |
77 |
83 | ````
84 |
85 | #### CloudTrax HTTP Authentication login
86 |
87 | ````
88 |
HTTP Authentication:
89 |
90 | Enter your login credentials:
91 |
92 |
93 |
100 | ````
101 |
102 | #### RADIUS server login
103 |
104 | ````
105 |
Radius Access:
106 |
107 | Enter your RADIUS credentials:
108 |
109 |
110 |
117 | ````
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/captive_portal/splash_pages/custom/images/edit-splash-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/splash_pages/custom/images/edit-splash-page.png
--------------------------------------------------------------------------------
/captive_portal/splash_pages/external/README.md:
--------------------------------------------------------------------------------
1 | # Externally Hosted splash pages
2 |
3 | * [Introduction](#intro)
4 | * [Overview](#overview)
5 | * [The redirect URL](#redir-url)
6 | * [Pre-login: responding to `'notyet'`](#notyet)
7 | * [Building a log-in form](#building-login-form)
8 | * [userurl](#userurl)
9 | * [Encoding the password](#encoding-pw)
10 | * [Redirecting the encoded password](#build-redir-url)
11 | * [Using 'redir' parameter](#redir)
12 | * [Session handling](#session)
13 | * [Post-authentication: responding to `'success'` and `'failed'`](#post-authentication)
14 |
15 |
16 | ## Introduction
17 |
18 | CloudTrax' external splash-page facility allows you to host your own splash pages and control every aspect of the log-in and authentication process. It requires you to code a splash-page implementation on a server that responds to the protocol described in this document. You also need to use the CloudTrax Dashboard to specify the URL address of your splash-page server code, as well as specify the type of back-end authentication service you are using, either [RADIUS](../../authentication/radius/) or [HTTP Authentication](../../authentication/http). PHP sample code is provided to help clarify the process.
19 |
20 | Here's a sample of what the Dashboard settings might look like for a typical setup:
21 |
22 | 
23 |
24 |
25 | ## Overview
26 | If you're using an externally hosted splash page, code running on the AP (Access Point) makes use of HTTP's *redirection capability* to invoke your splash server's code at several points during the Access Point's log-in/authentication process so that your sever can take appropropriate action. Redirection is the mechanism by which a web server can request a client to redirect its addressing attempt to a different URL than the one it used originally.
27 |
28 | One such use is when an AP recognizes that a client device is attempting to access a web page using an SSID on the AP for the first time. The AP intercepts the initial page request and returns an HTTP `302 Redirect` instruction to the web browser, causing it to redirect itself to the splash page instead. The special [redirect URL](#redir-url) that the AP constructs and provides as part of the Redirect has a query-string containing parameters of interest to the splash page server. In particular, a `'res'` parameter specifies the particular operation the server is being asked to carry out.
29 |
30 | This redirection mechanism is used on several occasions during login and authentication. The first, as mentioned above, is *pre-login*, which occurs on the initial detection of a new client's connection attempt. The other is *post-authentication*, which occurs once the back-end authentication service has signalled to the AP whether authentication was successful or failed. The AP then uses HTTP redirection to deliver a redirect URL to the splash page indicating the authentication status.
31 |
32 | Redirection is also used by the splash page server on one occasion to return control to the AP. See [Redirecting the encoding password](#XXXYYY).
33 |
34 | In both the pre-login and post-authentication case where the AP is redirecting to the splash page server, the redirection URL's query-string `'res'` parameter indicates which case it is. `'notyet'` indicates that the client has not yet been authenticated and is a signal that the splash page server needs to return a login page to the user. `'success'` and `'failed'` indicate the two possible outcomes of the attempt to authenticate, and `logout` is a response to a log-out message from the authentication service, if provided.
35 |
36 |
37 | ## The redirect URL
38 |
39 | Here's an example of the initial redirect URL constructed by the AP and redirected by the web client to your splash page server code to initiate the log-in sequence:
40 |
41 | ````
42 | http://example_server.com/uam_simple_server.php?res=notyet&uamip=10.255.224.1&uamport=8082&mac=64-76-BA-8A-D3-58&called=AC-86-74-3B-7A-C0&ssid=Howards%20Test%20Network%201&nasid=100.101.102.103&userurl=http%3A%2F%2Fwww.mlsite.net%2Fblog%2F%3Fp%3D1409&challenge=ACC28255A7A0122D682AFE0653F7440F0C19E6E9E89FABC03EA2CA82D791B90C
43 | ````
44 |
45 | The URL of the splash page host and its server code, http://example.com/uam_server.php, would have been specified by you in the CloudTrax Dashboard. The `'res'` parameter is set to `'notyet'`, telling the splash page server that the client has not yet been logged in, and that it should build and return a log-in form.
46 |
47 | The key-value pairs present in the redirect URL's query-string are as follows.
48 |
49 | parameter | description
50 | ----- | -----
51 | `res` | One of `'notyet'`, `'success'`, `'failed'`, or `'logoff'`.
52 | `uamip` | Internal address of the AP. Used by the splash page server to build a URL to the AP to return an encoded password to it. See [Redirecting the encoded password](#build-redir-url).
53 | `uamport` | AP port. Used together with `uamip` above to build a URL to the AP.
54 | `mac` | MAC address of the client device .
55 | `called` | MAC address of eth0 on the AP. This is the same MAC address that shows up in CloudTrax.
56 | `ssid` | SSID network name.
57 | `nasid` | ID of the Network Access Server, if one has been specified.
58 | `userurl` | url-encoded web page requested by the user, eg "http%3A%2F%2Fwww.google.com%2F".
59 | `challenge` | Provided by the AP. Used by server's log-in code, along with a shared secret, to produce an encoded password. The length of time the challenge is valid for is linked to the "Block duration of XX minutes" value listed in CloudTrax. The default value is 30 minutes.
60 |
61 | Let's walk through a sequence of messages to the splash page server and its responses as it proceeds through a typical log-in and authentication process.
62 |
63 |
64 | ## Pre-login: responding to `'notyet'`
65 |
66 | Your splash page server code needs to do a few things in response to receiving a `"res=notyet"` message from the AP. `'notyet'` indicates that the user has not yet been authenticated and marks the first step in the logging-in process. In response to receiving this message, your splash-page server needs to:
67 |
68 | 1. [build and return a log-in page to the user](#building-login-form),
69 | 2. [encode the password it gets back](#encoding-pw), and
70 | 3. [build a special redirection URL for the AP](#build-redir-url).
71 |
72 | Let's walk through all three steps in code.
73 |
74 |
75 | #### Building a log-in form
76 |
77 | Here's a snippet of code from [uam_simple_server.php](./code/php/uam_simple_server.php) that checks the contents of the query-string in the *transport URL* shipped to it from the AP, and accordingly builds a log-in form for the user to fill out.
78 |
79 | ```` php
80 |
81 |
82 |
83 |
86 |
Please Log in
87 |
96 |
101 |
102 |
103 | ````
104 |
105 | Note the use of hidden fields to hold the key-value pairs that were passed to the server in the transport URL's query-string. These will subsequently be POSTed to the "uam_handle_form.php" code that uses the `"challenge"` to encode the password.
106 |
107 |
108 | #### `'userurl'`
109 | The AP will redirect the user to the requested URL on conclusion of a successfully authenticated login. In that eventuality, the role of the splash-page server is concluded, and it will *not* be called again with the query-string parameter 'res' = 'success'. The `'userurl'` query-string parameter contains that redirection URL.
110 |
111 | You might notice by the way that the `action` parameter in our login form above dictates that we're POSTing to a separate PHP script, [uam_handle_form.php](./code/php/uam_handle_form.php), as already noted. It's not necessary to do this, and if in fact you omit the `action` parameter, you end up with the single-file server solution shown in [splash.php](./code/php/splash.php). Doing it the way we're doing it here makes the code a bit easier to follow in our opinion, but you can choose either implementation on which to base your own server.
112 |
113 |
114 | #### Encoding the password
115 |
116 | The first thing that [uam_handle_form.php](./code/php/uam_handle_form.php) needs to do is make some local copies of the variables that were POSTed to it. This just puts them in a slightly more convenient form for use. The `"challenge"` that was originally generated by the AP is passed into the function that's going to do the encoding, along with the `$uam_secret` at the top of the file. This is the shared secret we made available to CloudTrax when we initially configured the splash server in the Dashboard. Keep in mind that the length of time the challenge is valid for is linked to the "Block duration of XX minutes" value listed in CloudTrax. The user should attempt to login before this timeout is reach. The default value is 30 minutes.
117 |
118 |
119 | ```` php
120 | 0) {
151 | $crypt_secret = md5($hexchall . $secret, TRUE);
152 | $len_secret = 16;
153 | } else {
154 | $crypt_secret = $hexchall;
155 | $len_secret = strlen($hexchall);
156 | }
157 |
158 | /* simulate C style \0 terminated string */
159 | $plain .= "\x00";
160 | $crypted = '';
161 | for ($i = 0; $i < strlen($plain); $i++)
162 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret];
163 |
164 | $extra_bytes = 0;//rand(0, 16);
165 | for ($i = 0; $i < $extra_bytes; $i++)
166 | $crypted .= chr(rand(0, 255));
167 |
168 | return bin2hex($crypted);
169 | }
170 | ````
171 |
172 | Note also the use of PHP's [bitwise exclusive-or](http://php.net/manual/en/language.operators.bitwise.php) operator, `^`, in the line:
173 |
174 | ```` php
175 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret];
176 | ````
177 |
178 | The full source for [uam_handle_form.php](./code/php/uam_handle_form.php), by the way, contains some additional debugging gear we're not showing here. It might be useful if you want to view the contents of what was POSTed or the composition of the redirection URL it builds to send back to the Access Point. Which coincidentally is our next topic.
179 |
180 |
181 |
182 | #### Redirecting the encoded password
183 |
184 | The last thing your server needs to do in responding to `'notyet'` is to build a special URL containing the username and encoded password and return that URL to the AP so it can forward those on to the back-end authentication server.
185 |
186 | Since this logging-in process was initially started (what seems to be a very long time ago!) by the user attempting to browse to a new web page, the URL we're constructing would normally be returned back to the user's web browser, which would not be very useful. What we really want to do is return the URL to the AP instead.
187 |
188 | We do this by using the same HTTP redirection capability we [discussed earlier](#http-redirection), where a `302 Redirect` was used by the AP to call the splash-page server with a `notyet` message. Here we'll do the same thing but in reverse, allowing the AP to take over the flow of control once again.
189 |
190 | This is the reason for the existence of the two POSTed variables, `$uamip` and `$uamport`, which are used together to build the URL address of the AP, as in
191 |
192 | ```` php
193 | $redirect_url = "http://$uamip:$uamport/logon?" .
194 | "username=" . urlencode($username) .
195 | "&password=" . urlencode($encoded_password);
196 | ````
197 | This URL, when fully filled out, will look something like this:
198 |
199 | ````
200 | http://10.255.224.1:8082/logon?username=testuser&password=d14b2343e44f19a5d37fc83f68bc1daae123
201 | ````
202 | The IP address shown is an *internal* one (it's only for use within your local area network and is not addressable across the greater Internet), and may differ depending on your particular setup.
203 |
204 | Finally, the PHP directive that causes the actual redirection is this:
205 |
206 | ```` php
207 | header('Location: ' . $redirect_url);
208 | ````
209 | Note this only works correctly if the `Location` header is emitted before any *textual* output. In fact, this is true for *any* HTTP header, as noted in the PHP Manual: ["Send a raw HTTP Header"](http://php.net/manual/en/function.header.php):
210 |
211 | > Remember that header() must be called before any actual output is sent, either by normal HTML tags,
212 | > blank lines in a file, or from PHP.
213 |
214 |
215 | ####Using 'redir' parameter
216 | One final note on using the 'Location:' directive to achieve redirection: You can change the location of the final landing page, if you so desire, by adding a `'redir'` directive to the `$redirect_url` *before* issuing the above `header()` command. For example,
217 |
218 | ```` php
219 | $redirect_url .= "&redir=" . urlencode("http://myportal.example.com");
220 | ````
221 |
222 | if you use the `'redir'` parameter, the AP will automatically redirect to that URL and will not call your splash-page server with a 'res' = 'success' parameter (similar to the case [above](#userurl) when we discussed the use of "Redirection URL").
223 |
224 |
225 | #### Session handling
226 | Our form handler contains some PHP session-handling code,
227 |
228 | ```` php
229 | session_start();
230 | if(isset($_POST["userurl"])) {
231 | $_SESSION["userurl"] = $_POST["userurl"];
232 | } else {
233 | unset($_SESSION["userurl"]);
234 | }
235 | session_write_close();
236 | ````
237 |
238 | The purpose of this code is to maintain the value of `userurl` across web-server invocations. This makes `userurl` available to the "success"-handling code discussed below, where it is used, along with an HTML **<meta>** tag, to redirect the web browser to its final landing page, if that was specified by the user.
239 |
240 |
241 | ## Post-authentication: responding to `'success'` and `'failed'`
242 |
243 | ```` php
244 | # "res"==="notyet" case discussed above
245 | }
246 | else if ($res === "success") {
247 | $redir = $_SESSION["userurl"];
248 | if(isset($redir)) {
249 | echo "";
250 | echo '
';
251 | echo "";
252 | }
253 | else {
254 | echo "
Log-in successful!
";
255 | }
256 | }
257 | else if ($res === "failed") {
258 | echo "
Whoops, failed to authenticate
";
259 | }
260 | else {
261 | # ... logoff case remains
262 | }
263 | ````
264 |
265 | The two `if ($res === ...)` statements, for "success" and "failed", deliver the two possible results of the back-end authentication. If authentication was successfull, we use an HTML **<meta>** tag to redirect the web browser to its final landing page, getting the value of `userurl` from our `$_SESSION` dictionary. If authentication failed, our server simply notes the fact. In a more realistic scenario, we would have likely posted a log-in form explaining the failure and allowing the user to attempt to log-in again.
266 |
267 | Alternatively, in the event that the splash-page server had determined that the user had made too many attempts to log in at this point, we could have posted a message stating they'd been locked out for a certain amount of time.
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
--------------------------------------------------------------------------------
/captive_portal/splash_pages/external/click_to_enter/README.md:
--------------------------------------------------------------------------------
1 | # UAM Splash Authentication click-to-enter page
2 |
3 | The normal CloudTrax splash page allows a click-to-enter style authentication. UAM Splash Authentication does not directly allow this kind of page because the UAM page is designed to exchange a username and password. This article will show how UAM Splash Authentication can be used for click-to-enter style authentication.
4 |
5 | ## Click-to-Enter page
6 |
7 | A click-to-enter page can have many different elements. For example an e-mail field could be used that gets stored in a database. We assume that it is using an informative text for the user, a field (accept checkbox) which is evaluated by the backend and an submit button. Additional, different or even less fields are possible.
8 |
9 | The user is in this example expected to enable the checkbox and then press the OK button. The user should only get access when the UAM page server verified that the user accepted the TOS.
10 |
11 | ```
12 | ----------------------------------------
13 | | |
14 | | IMPORTANT TEXT |
15 | | EVEN MORE IMPORTANT TEXT |
16 | | FINE-PRINT |
17 | | |
18 | | [ ] accept TOS |
19 | | |
20 | | [OK] |
21 | | |
22 | ----------------------------------------
23 | ```
24 |
25 | ## Authentication process
26 |
27 | The basic idea is to use the username as auto-generated id and the password as signature for the username. Both can be used together to validate a user login.
28 |
29 | 
30 |
31 | The user is still automatically redirected to the remote login page when he tries to access the internet. But this time no username and password fields are shown. Instead a different set of fields are presented which he has to fill out. A press on the ok button submits the content of these fields back to the login server.
32 |
33 | The task of the login server is then to validate the forms. The UAM login page server has to generate a new username and password when the content is validated successfully. The username should be based on the client mac address and the current time. The password on the other hand should be used as signature and hard to guess. A good approach is to create the sha256_hmac of the username together with a unique secret that is only known to the server.
34 |
35 | Both username and password are then returned to the client browser. The browser of the client will submit the username and password automatically to the AP which then tries to validate them.
36 |
37 | The HTTP authentication backend is expected to first validate the username. If the username doesn't contain the mac of the current user or if the timestamp in the username is outside an acceptable range then the user has to be rejected. If the username looks valid then the signature of the username has to be generated with the same algorithm as used by the splash page. The authentication backend must only return a success message when the submitted password matches the generated signature.
38 |
39 | The client browser will then be redirected to the login page for the success message. The UAM remote login page can then display or redirect to any page it wants.
40 |
41 | ## Recommendations
42 |
43 | The accounting messages can be disabled via the udsplash ssid option "disable_accounting".
44 |
45 | ## Example
46 |
47 | ### Configuration in the cloudtrax dashboard:
48 |
49 | ```
50 | Splash page URL (UAM): http://1.2.3.4/test/uam.php
51 | Splash page secret (UAM): toosecretstring
52 | Splash page authentication URL (HTTP Auth): http://1.2.3.4/test/server.php
53 | Splash page authentication Secret (HTTP Auth): verysecretstring
54 | ```
55 |
56 | ### Runtime environment:
57 |
58 | ```
59 | Client MAC: 02:ba:de:af:fe:01
60 | Time (unix timestamp): 1440596666
61 | ```
62 |
63 | ### Sever configuration:
64 |
65 | ```
66 | Secret used for the signature: evenmoresecretstring
67 | ```
68 |
69 | ### UAM splashpage process
70 |
71 | The user should automatically be redirect to the splash page when its browser is trying to access a webpage.
72 |
73 | ```
74 | http://1.2.3.4/test/uam.php?res=notyet&...&mac=02-BA-DE-AF-FE-01&...
75 | ```
76 |
77 | The page has to validate its parameters and create a form with hidden fields to store things like the challenge or the client mac address.
78 |
79 | When the user has filled the form and pressed, the splash page has to create a new temporary username and password which will later be used to verify that the user of this client went through the splash page. Here we use SHA256_HMAC. Base64 is chosen instead of the normal hex representation to avoid that the generated password gets larger than the limit of 63 characters.
80 |
81 | ```php
82 | $username = 02-BA-DE-AF-FE-01 . '_' . 1440596666;
83 | $password = base64_encode(hash_hmac('sha256', $username, 'evenmoresecretstring', true));
84 | ```
85 |
86 | In this example the splash page would generate:
87 |
88 | ```
89 | Username: 02-BA-DE-AF-FE-01_1440596666
90 | Password: FuDiZi//mEFgleZkUW67L0ZtoaEjEgugLbi3nHCkZHw=
91 | ```
92 |
93 | ### Authentication Process
94 |
95 | The AP will try to decode the supplied password and create an authentication request with both username and (re-encrypted) password for the HTTP API server. First the server has to decode the password again.
96 |
97 | ```
98 | username: 02-BA-DE-AF-FE-01_1440596666
99 | password: FuDiZi//mEFgleZkUW67L0ZtoaEjEgugLbi3nHCkZHw=
100 | mac: 02:BA:DE:AF:FE:01
101 | ```
102 |
103 | The username has to be split first into a MAC and a timestamp again. The retrieved MAC has to match the MAC address of the authentication request.
104 |
105 | ```php
106 | /* split into mac and timestamp */
107 | $ret = preg_match('/^(?P
([A-F0-9]{2}\-){5}[A-F0-9]{2})_(?P[0-9]+)$/',
108 | $username, $matches);
109 | if (!$ret)
110 | return FALSE;
111 |
112 | /* check if mac is the same */
113 | if (str_replace(':', '-', $mac) != $matches['mac'])
114 | return FALSE;
115 | ```
116 |
117 | The time has to be checked to make sure it is not in the future nor too far in the past. A two minute window should be acceptable.
118 |
119 | ```php
120 | /* check if token comes from the future */
121 | $now = time();
122 | if ($now < $matches['timestamp'])
123 | return FALSE;
124 |
125 | /* check if token is too old */
126 | if (120 < ($now - $matches['timestamp']))
127 | return FALSE;
128 | ```
129 |
130 | As last step the signature has to be generated and has to be compared with the $password
131 |
132 | ```php
133 | /* generate signature and compare with the password */
134 | $signature = base64_encode(hash_hmac('sha256', $username, 'evenmoresecretstring', true));
135 | if ($signature != $password)
136 | return FALSE;
137 | ```
138 |
139 | A reply as explained in HTTP API documentation has to be sent when everything went fine. The AP will then send the user back to the splashpage which then can take appropriate actions.
140 |
141 | ### Source Code
142 |
143 | A complete example can be found under in the code directory.
144 |
145 | * Splashpage: uam.php
146 | * HTTP API server: server.php
147 |
--------------------------------------------------------------------------------
/captive_portal/splash_pages/external/click_to_enter/code/php/server.php:
--------------------------------------------------------------------------------
1 | 'REJECT',
24 | 'RA' => '0123456789abcdef0123456789abcdef',
25 | 'BLOCKED_MSG' => 'Rejected! This doesnt look like a valid request',
26 | );
27 |
28 |
29 | /**
30 | * print_dictionary - Print dictionary as encoded key-value pairs
31 | * @dict: Dictionary to print
32 | */
33 | function print_dictionary($dict)
34 | {
35 | foreach ($dict as $key => $value) {
36 | echo '"', rawurlencode($key), '" "', rawurlencode($value), "\"\n";
37 | }
38 | }
39 |
40 | /**
41 | * calculate_new_ra - calculate new request authenticator based on old ra, code
42 | * and secret
43 | * @dict: Dictionary containing old ra and code. new ra is directly stored in it
44 | * @secret: Shared secret between node and server
45 | */
46 | function calculate_new_ra(&$dict, $secret)
47 | {
48 | if (!array_key_exists('CODE', $dict))
49 | return;
50 |
51 | $code = $dict['CODE'];
52 |
53 | if (!array_key_exists('RA', $dict))
54 | return;
55 |
56 | if (strlen($dict['RA']) != 32)
57 | return;
58 |
59 | $ra = hex2bin($dict['RA']);
60 | if ($ra === FALSE)
61 | return;
62 |
63 | $dict['RA'] = hash('md5', $code . $ra . $secret);
64 | }
65 |
66 | /**
67 | * decode_password - decode encoded password to ascii string
68 | * @dict: dictionary containing request RA
69 | * @encoded: The encoded password
70 | * @secret: Shared secret between node and server
71 | *
72 | * Returns decoded password or FALSE on error
73 | */
74 | function decode_password($dict, $encoded, $secret)
75 | {
76 | if (!array_key_exists('RA', $dict))
77 | return FALSE;
78 |
79 | if (strlen($dict['RA']) != 32)
80 | return FALSE;
81 |
82 | $ra = hex2bin($dict['RA']);
83 | if ($ra === FALSE)
84 | return FALSE;
85 |
86 | if ((strlen($encoded) % 32) != 0)
87 | return FALSE;
88 |
89 | $bincoded = hex2bin($encoded);
90 |
91 | $password = "";
92 | $last_result = $ra;
93 |
94 | for ($i = 0; $i < strlen($bincoded); $i += 16) {
95 | $key = hash('md5', $secret . $last_result, TRUE);
96 | for ($j = 0; $j < 16; $j++)
97 | $password .= $key[$j] ^ $bincoded[$i + $j];
98 | $last_result = substr($bincoded, $i, 16);
99 | }
100 |
101 | $j = 0;
102 | for ($i = strlen($password); $i > 0; $i--) {
103 | if ($password[$i - 1] != "\x00")
104 | break;
105 | else
106 | $j++;
107 | }
108 |
109 | if ($j > 0) {
110 | $password = substr($password, 0, strlen($password) - $j);
111 | }
112 |
113 | return $password;
114 | }
115 |
116 | /**
117 | * validate_login - check if the login was valid for tos page
118 | *
119 | * Returns TRUE for valid logins, FALSE on errors
120 | */
121 | function validate_login($username, $password, $mac)
122 | {
123 | global $token_ttl;
124 | global $sigsecret;
125 |
126 | /* split into mac and timestamp */
127 | $ret = preg_match('/^(?P([A-F0-9]{2}\-){5}[A-F0-9]{2})_(?P[0-9]+)$/',
128 | $username, $matches);
129 | if (!$ret)
130 | return FALSE;
131 |
132 | /* check if mac is the same */
133 | if (str_replace(':', '-', $mac) != $matches['mac'])
134 | return FALSE;
135 |
136 | /* check if token comes from the future */
137 | $now = time();
138 | if ($now < $matches['timestamp'])
139 | return FALSE;
140 |
141 | /* check if token is too old */
142 | if ($token_ttl < ($now - $matches['timestamp']))
143 | return FALSE;
144 |
145 | /* generate signature and compare with the password */
146 | $signature = base64_encode(hash_hmac('sha256', $username, $sigsecret, true));
147 | if ($signature != $password)
148 | return FALSE;
149 |
150 | return TRUE;
151 | }
152 |
153 | /* copy request authenticator */
154 | if (array_key_exists('ra', $_GET) && strlen($_GET['ra']) == 32 && ($ra = hex2bin($_GET['ra'])) !== FALSE && strlen($ra) == 16) {
155 | $response['RA'] = $_GET['ra'];
156 | }
157 |
158 | /* decode password when available */
159 | $password = FALSE;
160 | if (array_key_exists('username', $_GET) && array_key_exists('password', $_GET))
161 | $password = decode_password($response, $_GET['password'], $secret);
162 |
163 | /* store mac when available */
164 | $mac = FALSE;
165 | if (array_key_exists('mac', $_GET))
166 | $mac = $_GET['mac'];
167 |
168 | /* decode request */
169 | if (array_key_exists('type', $_GET)) {
170 | $type = $_GET['type'];
171 |
172 | switch ($type) {
173 | case 'login':
174 | if ($password === FALSE)
175 | break;
176 |
177 | if ($mac === FALSE)
178 | break;
179 |
180 | if (validate_login($_GET['username'], $password, $mac) === TRUE) {
181 | unset($response['BLOCKED_MSG']);
182 | $response['CODE'] = "ACCEPT";
183 | $response['SECONDS'] = 3600;
184 | $response['DOWNLOAD'] = 2000;
185 | $response['UPLOAD'] = 800;
186 | } else {
187 | $response['BLOCKED_MSG'] = "Invalid username or password";
188 | }
189 | break;
190 | case 'status':
191 | if ($mac === FALSE)
192 | break;
193 |
194 | if ($mac == '5C:0A:5B:4E:6A:C5') {
195 | unset($response['BLOCKED_MSG']);
196 | $response['CODE'] = "ACCEPT";
197 | $response['SECONDS'] = 120;
198 | $response['DOWNLOAD'] = 3000;
199 | $response['UPLOAD'] = 400;
200 | } else {
201 | $response['BLOCKED_MSG'] = "Unknown Client";
202 | }
203 | break;
204 | case 'acct':
205 | case 'logout':
206 | if ($mac === FALSE)
207 | break;
208 | unset($response['BLOCKED_MSG']);
209 | $response['CODE'] = "OK";
210 | break;
211 | };
212 | }
213 |
214 | /* calculate new request authenticator based on answer and request -> send it out */
215 | calculate_new_ra($response, $secret);
216 | print_dictionary($response);
217 |
218 | ?>
219 |
--------------------------------------------------------------------------------
/captive_portal/splash_pages/external/click_to_enter/code/php/uam.php:
--------------------------------------------------------------------------------
1 | 0) {
32 | $crypt_secret = md5($hexchall . $secret, TRUE);
33 | $len_secret = 16;
34 | } else {
35 | $crypt_secret = $hexchall;
36 | $len_secret = strlen($hexchall);
37 | }
38 |
39 | /* simulate C style \0 terminated string */
40 | $plain .= "\x00";
41 | $crypted = '';
42 | for ($i = 0; $i < strlen($plain); $i++)
43 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret];
44 |
45 | $extra_bytes = rand(0, 16);
46 | for ($i = 0; $i < $extra_bytes; $i++)
47 | $crypted .= chr(rand(0, 255));
48 |
49 | return bin2hex($crypted);
50 | }
51 |
52 | /* WARNING better use http_redirect from pecl_http for this */
53 | function redirect($url, $statusCode = 302)
54 | {
55 | header('Location: ' . $url, true, $statusCode);
56 | }
57 |
58 | function main()
59 | {
60 | global $secret;
61 | global $sigsecret;
62 |
63 | foreach (array('res', 'uamip', 'uamport', 'mac') as $req) {
64 | if (!array_key_exists($req, $_GET)) {
65 | header("Content-Type: text/plain");
66 | print("No ". $req ." found\n");
67 | return;
68 | }
69 | }
70 |
71 | switch ($_GET['res']) {
72 | case 'notyet':
73 | case 'failed':
74 | /* continue after this block */
75 | break;
76 | case 'logoff':
77 | header("Content-Type: text/plain");
78 | print("Bye\n");
79 | return;
80 | case 'success':
81 | header("Content-Type: text/plain");
82 | print("Congrats. You are now logged in\n");
83 | return;
84 | default:
85 | header("Content-Type: text/plain");
86 | print("Sorry, I don't understand you\n");
87 | return;
88 | }
89 |
90 | /* autogenerate a username and password */
91 | $username = $_GET['mac'] . '_' . time();
92 | $password = base64_encode(hash_hmac('sha256', $username, $sigsecret, true));
93 |
94 | if (!array_key_exists('challenge', $_GET)) {
95 | /* this should not happen and is not part of the specification */
96 | $pw_crypt = urlencode($password);
97 | } else {
98 | $pw_crypt = encode_password_challenge($password,
99 | $_GET['challenge'],
100 | $secret);
101 | }
102 |
103 | if ($pw_crypt === FALSE) {
104 | header("Content-Type: text/plain");
105 | print("Failed to encode the password\n");
106 | return;
107 | }
108 |
109 |
110 | /* WARNING this check should use some extra token which changes often
111 | * to avoid users to automate the form submission with zero effort
112 | */
113 | if (array_key_exists('accept', $_GET) && $_GET['accept'] == 'accept') {
114 | redirect('http://'. $_GET['uamip'] . ':' . $_GET['uamport'] . '/logon?username=' . urlencode($username) . '&password=' . $pw_crypt);
115 | } else {
116 | render_page();
117 | }
118 | }
119 |
120 | /* WARNING better use proper template system - this is just a quick hack for this example */
121 | function render_page()
122 | {
123 | ?>
124 | '; ?>
125 |
126 |
127 |
128 | Splashpage
129 |
130 |
131 |
132 |
133 | TOS
134 |
135 | Lorem ipsum dolor sit amet, reque tation ad duo, illud
136 | aperiam labores eam ad, legere ignota nostrum ea duo. In sea
137 | etiam congue decore. Illud tantas corrumpit est in, sale prima
138 | error id mei. Ex his fugit aeterno, an natum tincidunt
139 | abhorreant sea, et eos modus convenire signiferumque. Ei eum
140 | iuvaret reprehendunt. Aliquid tacimates quo at, vix dicta
141 | scribentur consequuntur eu.
142 |
143 | Munere omnesque ei ius. Aliquam nominavi mei te, eum zril
144 | facete qualisque at, dicta hendrerit consequuntur ei nec. Odio
145 | integre intellegebat ius ea, agam diceret nominati eu nec, no
146 | menandri persecuti quaerendum eos. Eu latine explicari
147 | voluptaria vel. Qui mucius luptatum id. Vis in volumus
148 | scribentur, ad eam vulputate definitiones, ei nec exerci
149 | moderatius posidonium.
150 |
151 |
171 |
172 |
173 |
174 |
180 |
181 |
--------------------------------------------------------------------------------
/captive_portal/splash_pages/external/click_to_enter/images/2015-08-26_Click-To-Enter_nodb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/splash_pages/external/click_to_enter/images/2015-08-26_Click-To-Enter_nodb.png
--------------------------------------------------------------------------------
/captive_portal/splash_pages/external/code/php/README.md:
--------------------------------------------------------------------------------
1 | Files shown here demonstrate two slightly different ways of implementing a simple, externally-hosted splash page implementation using the UAM protocol. See [Externally Hosted Splash Pages](../../README.md).
2 |
--------------------------------------------------------------------------------
/captive_portal/splash_pages/external/code/php/splash.php:
--------------------------------------------------------------------------------
1 | 0) {
14 | $crypt_secret = md5($hexchall . $secret, TRUE);
15 | $len_secret = 16;
16 | } else {
17 | $crypt_secret = $hexchall;
18 | $len_secret = strlen($hexchall);
19 | }
20 |
21 | /* simulate C style \0 terminated string */
22 | $plain .= "\x00";
23 | $crypted = '';
24 | for ($i = 0; $i < strlen($plain); $i++)
25 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret];
26 |
27 | $extra_bytes = 0;//rand(0, 16);
28 | for ($i = 0; $i < $extra_bytes; $i++)
29 | $crypted .= chr(rand(0, 255));
30 |
31 | return bin2hex($crypted);
32 | }
33 |
34 | function print_logon_form() {
35 | ?>
36 |
37 |
38 | Example Remote Splash Page
39 |
40 |
49 |
50 |
51 |
57 |
58 |
59 |
60 | Example Remote Splash Page
61 | ';
64 | }
65 | ?>
66 |
67 |
68 | Welcome! You will be redirected to your destination momentarily';
71 | } else {
72 | echo 'Welcome!
';
73 | }
74 | ?>
75 |
76 |
77 |
82 |
83 |
84 | Example Remote Splash Page
85 | Authentication Failed
86 |
87 |
92 |
93 |
94 | Example Remote Splash Page
95 | GoodBye
96 |
97 |
98 | 0) {
15 | $crypt_secret = md5($hexchall . $secret, TRUE);
16 | $len_secret = 16;
17 | } else {
18 | $crypt_secret = $hexchall;
19 | $len_secret = strlen($hexchall);
20 | }
21 |
22 | /* simulate C style \0 terminated string */
23 | $plain .= "\x00";
24 | $crypted = '';
25 | for ($i = 0; $i < strlen($plain); $i++)
26 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret];
27 |
28 | $extra_bytes = 0;//rand(0, 16);
29 | for ($i = 0; $i < $extra_bytes; $i++)
30 | $crypted .= chr(rand(0, 255));
31 |
32 | return bin2hex($crypted);
33 | }
34 |
35 |
36 | $username = $_POST["username"];
37 | $password = $_POST["password"];
38 | $uamip = $_POST["uamip"];
39 | $uamport = $_POST["uamport"];
40 | $challenge = $_POST["challenge"];
41 |
42 | $encoded_password = encode_password($password, $challenge, $uam_secret);
43 |
44 | $redirect_url = "http://$uamip:$uamport/logon?" .
45 | "username=" . urlencode($username) .
46 | "&password=" . urlencode($encoded_password);
47 |
48 | # point them toward a different landing page if you want ...
49 | # (couldn't get this working)
50 | #$redirect_url .= "&redir=" . urlencode("http://www.nytimes.com");
51 |
52 | session_start();
53 | if(isset($_POST["userurl"])) {
54 | $_SESSION["userurl"] = $_POST["userurl"];
55 | } else {
56 | unset($_SESSION["userurl"]);
57 | }
58 | session_write_close();
59 |
60 |
61 | if ($DEBUG) {
62 | echo "userurl: {" . $_SESSION['userurl'] . "}
";
63 | echo "REDIRECT_URL: {\"" . $redirect_url . "\"}
";
64 |
65 | echo "POST data:";
66 | echo "";
67 | print_r($_POST);
68 | echo "
" . "
";
69 |
70 | echo "SERVER data:";
71 | echo "";
72 | print_r($_SERVER);
73 | echo "
";
74 | } else {
75 | header('Location: ' . $redirect_url);
76 | exit();
77 | }
78 |
79 | ?>
80 |
--------------------------------------------------------------------------------
/captive_portal/splash_pages/external/code/php/uam_simple_server.php:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
";
12 | echo "res: {\"" . $_GET["res"] . "\"}
";
13 | echo "request_uri: {\"" . $_SERVER["REQUEST_URI"] . "\"}
";
14 |
15 | echo "SERVER data:";
16 | echo "";
17 | print_r($_SERVER);
18 | echo "
";
19 | }
20 | ?>
21 |
22 |
27 | Please Log in
28 |
37 | ";
43 | echo '';
44 | echo "";
45 | }
46 | else {
47 | echo "Log-in successful!
";
48 | }
49 | }
50 | else if ($res === "failed") {
51 | echo "Whoops, failed to authenticate
";
52 | }
53 | else if ($res === "logoff") {
54 | echo "Logging off ...
";
55 | }
56 | else {
57 | echo "Oops!, bad 'res' parameter
";
58 | }
59 | ?>
60 |
61 |
62 |