Test Setup Failed
Push — master ( bc80e5...d6a814 )
by Tomasz
06:32
created

DeviceLinuxSh::mkSsidList()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 9
rs 10
cc 3
nc 3
nop 0
1
<?php
2
/*
3
 * *****************************************************************************
4
  * Contributions to this work were made on behalf of the GÉANT project, a
5
  * project that has received funding from the European Union’s Framework
6
  * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
7
  * Horizon 2020 research and innovation programme under Grant Agreements No.
8
  * 691567 (GN4-1) and No. 731122 (GN4-2).
9
  * On behalf of the aforementioned projects, GEANT Association is the sole owner
10
  * of the copyright in all material which was developed by a member of the GÉANT
11
  * project. GÉANT Vereniging (Association) is registered with the Chamber of
12
  * Commerce in Amsterdam with registration number 40535155 and operates in the
13
  * UK as a branch of GÉANT Vereniging.
14
  *
15
  * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands.
16
  * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
17
  *
18
  * License: see the web/copyright.inc.php file in the file structure or
19
  *          <base_url>/copyright.php after deploying the software
20
  */
21
/**
22
 * This file creates Linux installers
23
 *
24
 * @author Tomasz Wolniewicz <[email protected]>
25
 * @author Robert Grätz <[email protected]>
26
 *
27
 * @package ModuleWriting
28
 */
29
namespace devices\linux;
30
use Exception;
31
class DeviceLinuxSh extends \core\DeviceConfig {
32
    /**
33
     * constructor. Sets supported EAP methods.
34
     */
35
    final public function __construct() {
36
        parent::__construct();
37
        $this->setSupportedEapMethods([\core\common\EAP::EAPTYPE_PEAP_MSCHAP2, \core\common\EAP::EAPTYPE_TTLS_PAP, \core\common\EAP::EAPTYPE_TTLS_MSCHAP2, \core\common\EAP::EAPTYPE_TLS, \core\common\EAP::EAPTYPE_SILVERBULLET]);
38
        $this->specialities['media:openroaming'] = _("This device does not support provisioning of OpenRoaming.");
39
        $this->specialities['media:consortium_OI'] = _("This device does not support provisioning of Passpoint networks.");
40
41
    }
42
43
    /**
44
     * create the actual installer script
45
     *
46
     * @return string filename of the generated installer
47
     * @throws Exception
48
     *
49
     */
50
    public function writeInstaller() {
51
        $installerPath = $this->installerBasename.".sh";
52
        $this->copyFile("eduroam_linux_main.sh", $installerPath);
53
54
        if ( !file_exists($installerPath) ) {
55
            throw new Exception('File not found.');
56
        }
57
58
        if (!$installer = fopen($installerPath, "a")) {
59
            throw new Exception("Unable to open installer file for writing!");
60
        }
61
62
        try {
63
            fwrite($installer, "\n\n");
64
            fseek($installer, 0, SEEK_END);
65
            $this->writeMessages($installer);
66
            $this->writeConfigVars($installer);
67
            fwrite($installer, 'printf -v INIT_INFO "$INIT_INFO_TMP" "$ORGANISATION" "$EMAIL" "$URL"'."\n");
68
            fwrite($installer, 'printf -v INIT_CONFIRMATION "$INIT_CONFIRMATION_TMP" "$ORGANISATION"'."\n\n");
69
            fwrite($installer, 'main "$@"; exit'."\n");
70
        } catch (Exception $e) {
71
            echo 'Error message: ' .$e->getMessage();
72
        } finally {
73
            fclose($installer);
74
            return($installerPath);
75
        }
76
    }
77
78
    /**
79
     * produces the HTML text to be displayed when clicking on the "help" button
80
     * besides the download button.
81
     *
82
     * @return string
83
     */
84
    public function writeDeviceInfo() {
85
        \core\common\Entity::intoThePotatoes();
86
        $out = sprintf(_("The installer is in the form of a Python script. It will try to configure %s under NetworkManager and if this is either not appropriate for your system or your version of NetworkManager is too old, a wpa_supplicant config file will be created instead."), \config\ConfAssistant::CONSORTIUM['display_name']);
87
        $out .= "<p>"._("The installer will configure access to:")." <strong>";
88
        $out .= implode('</strong>, <strong>', array_keys($this->attributes['internal:networks']));
89
        $out .= '</strong><p>';
90
91
        $out .= _("The installer will create cat_installer sub-directory in your config directory (possubly the .config in your home directory) and will copy your server certificates there.");
92
        if ($this->selectedEap == \core\common\EAP::EAPTYPE_TLS) {
93
            $out .= _("In order to connect to the network you will need a personal certificate in the form of a p12 file. You should obtain this certificate from your organisation. Consult the support page to find out how this certificate can be obtained. Such certificate files are password protected. You should have both the file and the password available during the installation process. Your p12 file will also be copied to the cat_installer directory.");
94
        } elseif ($this->selectedEap != \core\common\EAP::EAPTYPE_SILVERBULLET) {
95
            $out .= _("In order to connect to the network you will need an account from your organisation. You should consult the support page to find out how this account can be obtained. It is very likely that your account is already activated.");
96
            $out .= "<p>";
97
            $out .= _("You will be requested to enter your account credentials during the installation. This information will be saved so that you will reconnect to the network automatically each time you are in the range.");
98
        }
99
        // nothing to say if we are doing silverbullet.
100
        $out .= "<p>";        
101
        \core\common\Entity::outOfThePotatoes();
102
        return $out;
103
    }
104
105
    /**
106
     * writes a line of Python code into the installer script
107
     *
108
     * @param resource $file   the file handle
109
     * @param string   $name   config item to write
110
     * @param string   $text   text to write
111
     * @return void
112
     */
113
    private function writeConfigLine($file, $name, $text) {
114
        $out = $name.'="'.$text.'"'."\n";
115
//        fwrite($file, wordwrap($out, 70, "\n"));
116
        fwrite($file, $out);
117
118
    }
119
120
    /**
121
     * localises the user messages and writes them into the file
122
     *
123
     * @param resource $file the file resource of the installer script
124
     * @return void
125
     */
126
    private function writeMessages($file) {
127
        \core\common\Entity::intoThePotatoes();
128
        $messages = [
129
        'QUIT'=> _("Really quit?"),
130
        'USERNAME_PROMPT'=> _("enter your userid"),
131
        'ENTER_PASSWORD' => _("enter password"),
132
        'ENTER_IMPORT_PASSWORD' => _("enter your import password"),
133
        'INCORRECT_PASSWORD' => _("incorrect password"),
134
        'REPEAT_PASSWORD' => _("repeat your password"),
135
        'PASSWORD_DIFFER'=> _("passwords do not match"),
136
        'INSTALLATION_FINISHED' => _("Installation successful"),
137
        'CAT_DIR_EXISTS' => _("Directory %s exists; some of its files may be overwritten."),
138
        'CONTINUE' => _("Continue?"),
139
        'NM_NOT_SUPPORTED' => _("This NetworkManager version is not supported"),
140
        'CERT_ERROR' => _("Certificate file not found, looks like a CAT error"),
141
        'UNKNOWN_VERSION' => _("Unknown version"),
142
        'DBUS_ERROR' => _("DBus connection problem, a sudo might help"),
143
        'YES' => _("Y"),
144
        'NO' => _("N"),
145
        'P12_FILTER' => _("personal certificate file (p12 or pfx)"),
146
        'ALL_FILTER' => _("All files"),
147
        'P12_TITLE' => _("personal certificate file (p12 or pfx)"),
148
        'SAVE_WPA_CONF' => _("NetworkManager configuration failed, but we may generate a wpa_supplicant configuration file if you wish. Be warned that your connection password will be saved in this file as clear text."),
149
        'SAVE_WPA_CONFIRM' => _("Write the file"),
150
        'WRONG_USERNAME_FORMAT' =>_("Error: Your username must be of the form 'xxx@institutionID' e.g. '[email protected]'!"),
151
        'WRONG_REALM' => _("Error: your username must be in the form of 'xxx@%s'. Please enter the username in the correct format."),
152
        'WRONG_REALM_SUFFIX' => _("Error: your username must be in the form of 'xxx@institutionID' and end with '%s'. Please enter the username in the correct format."),
153
        'USER_CERT_MISSING' => _("personal certificate file not found"),
154
        ];
155
        foreach ($messages as $name => $value) {
156
            $this->writeConfigLine($file, $name, $value);
157
        }
158
        \core\common\Entity::outOfThePotatoes();
159
    }
160
161
    /**
162
     * writes configuration variables into the installer script
163
     *
164
     * @param resource $file the file handle
165
     * @return void
166
     */
167
    private function writeConfigVars($file) {
168
        $eapMethod = \core\common\EAP::eapDisplayName($this->selectedEap);
169
        $contacts = $this->mkSupportContacts();
170
        $tou = $this->mkUserConsent();
171
        $outerId = $this->determineOuterIdString();
172
        $config = [
173
            'ORGANISATION' => $this->attributes['general:instname'][0],
174
            'PROFILE_NAME' => $this->attributes['profile:name'][0],
175
            'URL' => $contacts['url'],
176
            'EMAIL' => $contacts['email'],
177
            'TITLE' => "eduroam CAT",
178
            'SERVER_MATCH' => $this->glueServerNames(),
179
            'EAP_OUTER' => $eapMethod['OUTER'],
180
            'EAP_INNER' => $eapMethod['INNER'],
181
            'INIT_INFO_TMP' => $this->mkIntro(),
182
            'INIT_CONFIRMATION_TMP' => $this->mkProfileConfirmation(),
183
            // 'sb_user_file' => $this->mkSbUserFile(),
184
        ];
185
186
        $configRaw = [
187
            'SSIDS' => $this->mkSsidList(),
188
            'DEL_SSIDS' => $this->mkDelSsidList(),
189
            'ALTSUBJECT_MATCHES' => $this->mkSubjectAltNameList(),
190
        ];
191
192
        if ($this->selectedEap == \core\common\EAP::EAPTYPE_TLS && isset($this->attributes['eap-specific:tls_use_other_id']) && $this->attributes['eap-specific:tls_use_other_id'][0] == 'on') {
193
            $configRaw['USE_OTHER_TLS_ID'] = true;
194
        }
195
        else {
196
            $configRaw['USE_OTHER_TLS_ID'] = false;
197
        }
198
199
        if ($outerId !== NULL) {
200
            $configRaw['ANONYMOUS_IDENTITY'] = '"'.$outerId.'"';
201
        }
202
203
        if (!empty($this->attributes['internal:realm'][0])) {
204
           $config['USER_REALM'] = $this->attributes['internal:realm'][0];
205
        }
206
207
        if(!empty($this->attributes['internal:hint_userinput_suffix'][0]) && $this->attributes['internal:hint_userinput_suffix'][0] == 1) {
208
            $configRaw['HINT_USER_INPUT'] = true;
209
        }
210
211
        if(!empty($this->attributes['internal:verify_userinput_suffix'][0]) && $this->attributes['internal:verify_userinput_suffix'][0] == 1) {
212
            $configRaw['VERIFY_USER_REALM_INPUT'] = true;
213
        } else {
214
            $configRaw['VERIFY_USER_REALM_INPUT'] = false;
215
        }
216
217
        foreach ($config as $name => $value) {
218
            $this->writeConfigLine($file, $name, $value);
219
        }
220
221
        foreach ($configRaw as $name => $value) {
222
            fwrite($file, $name ."=". $value."\n");
223
        }
224
225
        if ($tou === '') {
226
            fwrite($file, "TOU=\"\"\n");
227
        } else {
228
            fwrite($file, "TOU=\"".$tou."\"\n");
229
        }
230
231
        fwrite($file, "CA_CERTIFICATE=\"".$this->mkCAfile()."\"\n");
232
        $sbUserFile = $this->mkSbUserFile();
233
        if ($sbUserFile !== '') {
234
            fwrite($file, "SB_USER_FILE=\"".$sbUserFile."\"\n");
235
        }
236
    }
237
238
    /**
239
     * coerces the list of EAP server names into a single string
240
     *
241
     * @return string
242
     */
243
    private function glueServerNames() {
244
        $serverList = $this->attributes['eap:server_name'];
245
        if (!$serverList) {
246
            return '';
247
        }
248
        $A0 = array_reverse(explode('.', array_shift($serverList)));
249
        $B = $A0;
250
        foreach ($serverList as $oneServer) {
251
            $A = array_reverse(explode('.', $oneServer));
252
            $B = array_intersect_assoc($A0, $A);
253
            $A0 = $B;
254
        }
255
        return implode('.', array_reverse($B));
256
    }
257
258
    /**
259
     * generates the list of support contacts
260
     *
261
     * @return array
262
     */
263
    private function mkSupportContacts() {
264
        $url = (!empty($this->attributes['support:url'][0])) ? $this->attributes['support:url'][0] : $this->support_url_substitute;
265
        $email = (!empty($this->attributes['support:email'][0])) ? $this->attributes['support:email'][0] : $this->support_email_substitute;
266
        return ['url'=>$url, 'email'=>$email];
267
    }
268
269
    /**
270
     * generates the list of subjectAltNames to configure
271
     *
272
     * @return string
273
     */
274
    private function mkSubjectAltNameList() {
275
        $serverList = $this->attributes['eap:server_name'];
276
        if (!$serverList) {
277
            return '';
278
        }
279
        $out = '';
280
        foreach ($serverList as $oneServer) {
281
            if ($out) {
282
                $out .= ', ';
283
            }
284
            $out .= "'DNS:$oneServer'";
285
        }
286
        return "(".$out. ")";
287
    }
288
289
    /**
290
     * generates the list of SSIDs to configure
291
     *
292
     * @return string
293
     */
294
    private function mkSsidList() {
295
        $networks = $this->attributes['internal:networks'];
296
        $outArray = [];
297
        foreach ($networks as $network => $networkDetails) {
298
            if (!empty($networkDetails['ssid'])) {
299
                $outArray = array_merge($outArray, $networkDetails['ssid']);
300
            }
301
        }
302
        return "['".implode("', '", $outArray)."']";
303
    }
304
305
    /**
306
     * generates the list of SSIDs to delete from the system
307
     *
308
     * @return string
309
     */
310
    private function mkDelSsidList() {
311
        $outArray = [];
312
        $delSSIDs = $this->attributes['internal:remove_SSID'];
313
        foreach ($delSSIDs as $ssid => $cipher) {
314
            if ($cipher == 'DEL') {
315
                $outArray[] = "'$ssid'";
316
            }
317
        }
318
        return '('.implode(', ', $outArray).')';
319
    }
320
321
    /**
322
     * creates a blob containing all CA certificates
323
     *
324
     * @return string
325
     */
326
    private function mkCAfile(){
327
        $out = '';
328
        $cAlist = $this->attributes['internal:CAs'][0];
329
        foreach ($cAlist as $oneCa) {
330
            $out .= $oneCa['pem'];
331
        }
332
        return $out;
333
    }
334
335
    /**
336
     * generates the welcome text
337
     *
338
     * @return string
339
     */
340
    private function mkIntro() {
341
        \core\common\Entity::intoThePotatoes();
342
        $out = _("This installer has been prepared for %s").'\n\n'._("More information and comments:").'\n\nE-Mail: %s\nWWW: %s\n\n' .
343
            _("Installer created with software from the GEANT project.");
344
        \core\common\Entity::outOfThePotatoes();
345
        return $out;
346
    }
347
348
    /**
349
     * generates text for the user consent dialog box, if any
350
     *
351
     * @return string
352
     */
353
    private function mkUserConsent() {
354
        $out = '';
355
        if (isset($this->attributes['support:info_file'])) {
356
            if ($this->attributes['internal:info_file'][0]['mime'] == 'txt') {
357
                $out = $this->attributes['support:info_file'][0];
358
            }
359
        }
360
        return $out;
361
    }
362
363
    /**
364
     * generates the warning that the account will only work for inst members
365
     *
366
     * @return string
367
     */
368
    private function mkProfileConfirmation() {
369
        \core\common\Entity::intoThePotatoes();
370
        if ($this->attributes['internal:profile_count'][0] > 1) {
371
            $out = _("This installer will only work properly if you are a member of %s and the user group: %s.");
372
        } else {
373
            $out = _("This installer will only work properly if you are a member of %s.");
374
        }
375
        \core\common\Entity::outOfThePotatoes();
376
        return $out;
377
    }
378
379
380
    /**
381
     * generates the client certificate data for Silberbullet installers
382
     *
383
     * @return string
384
     */
385
    private function mkSbUserFile() {
386
        if ($this->selectedEap == \core\common\EAP::EAPTYPE_SILVERBULLET) {
387
            return chunk_split(base64_encode($this->clientCert["certdata"]), 64, "\n");
388
        }
389
        return "";
390
    }
391
392
}
393