Test Failed
Push — master ( 83b526...bd639c )
by Stefan
23:38
created

DeviceW8W10::setupEapConfig()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 23
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 21
c 1
b 0
f 0
dl 0
loc 23
rs 8.9617
cc 6
nc 16
nop 0
1
<?php
2
3
/*
4
 * *****************************************************************************
5
 * Contributions to this work were made on behalf of the GÉANT project, a 
6
 * project that has received funding from the European Union’s Framework 
7
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
8
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
9
 * 691567 (GN4-1) and No. 731122 (GN4-2).
10
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
11
 * of the copyright in all material which was developed by a member of the GÉANT
12
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
13
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
14
 * UK as a branch of GÉANT Vereniging.
15
 * 
16
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
17
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
18
 *
19
 * License: see the web/copyright.inc.php file in the file structure or
20
 *          <base_url>/copyright.php after deploying the software
21
 */
22
23
/**
24
 * This file creates MS Windows 8 and 10 installers
25
 * It supports EAP-TLS, TTLS (both native and GEANTLink), PEAP.
26
 * Other EAP methods could be added.
27
 * 
28
 * The file is an interface between the global CAT system and individual EAP
29
 * methods modules. It also performs global operations like preparing
30
 * and saving cerificates and generating the installers.
31
 * 
32
 * Adding a new EAP handler requires defining an extension of the MsEapProfile
33
 * class. Such an extension is required to define a public getConfig method
34
 * returning a valid Windows XML <Config> element.
35
 * Extensions to Files/common.inc will also be required.
36
 * 
37
 * @author Tomasz Wolniewicz <[email protected]>
38
 *
39
 * @package ModuleWriting
40
 */
41
42
namespace devices\ms;
43
use Exception;
44
45
class DeviceW8W10 extends \devices\ms\WindowsCommon
46
{
47
    public function __construct()
48
    {
49
        parent::__construct();
50
        \core\common\Entity::intoThePotatoes();
51
        $this->setSupportedEapMethods([
52
            \core\common\EAP::EAPTYPE_TLS,
53
            \core\common\EAP::EAPTYPE_PEAP_MSCHAP2,
54
            \core\common\EAP::EAPTYPE_TTLS_PAP,
55
            \core\common\EAP::EAPTYPE_TTLS_MSCHAP2,
56
            \core\common\EAP::EAPTYPE_SILVERBULLET
57
        ]);
58
        $this->profileNames = [];
59
        $this->specialities['internal:use_anon_outer'][serialize(\core\common\EAP::EAPTYPE_PEAP_MSCHAP2)] = _("Anonymous identities do not use the realm as specified in the profile - it is derived from the suffix of the user's username input instead.");
60
        $this->specialities['media:openroaming'] = _("While OpenRoaming can be configured, it is possible that the Wi-Fi hardware does not support it; then the network definition is ignored.");
61
        $this->specialities['media:consortium_OI'] = _("While Passpoint networks can be configured, it is possible that the Wi-Fi hardware does not support it; then the network definition is ignored.");
62
        \core\common\Entity::outOfThePotatoes();
63
    } 
64
    
65
    /**
66
     * create the actual installer executable
67
     * 
68
     * @return string filename of the generated installer
69
     *
70
     */  
71
    public function writeInstaller()
72
    {
73
        \core\common\Entity::intoThePotatoes();
74
        $this->prepareInstallerLang();
75
        $this->setupEapConfig();
76
        $setWired = isset($this->attributes['media:wired'][0]) && $this->attributes['media:wired'][0] == 'on' ? 1 : 0;
77
        $this->iterator = 0;
78
        $fcontentsProfile = '';
79
        $this->createProfileDir();
80
        foreach ($this->attributes['internal:networks'] as $networkName => $oneNetwork) {
81
            if ($this::separateHS20profiles === true) {
82
                $fcontentsProfile .= $this->saveNetworkProfileSeparateHS($networkName, $oneNetwork);
83
            } else {
84
                $fcontentsProfile .= $this->saveNetworkProfileJoinedHS($networkName, $oneNetwork);
85
            }
86
        }
87
        file_put_contents('profiles.nsh', $fcontentsProfile);
88
        $delSSIDs = $this->attributes['internal:remove_SSID'];
89
        $delProfiles = [];
90
        foreach ($delSSIDs as $ssid => $cipher) {
91
            if ($cipher == 'DEL') {
92
                $delProfiles[] = $ssid;
93
            }
94
            if ($cipher == 'TKIP') {
95
                $delProfiles[] = $ssid.' (TKIP)';
96
            }
97
        }
98
        // this removes the profile container that we used in CAT 2.1 and removed in 2.1.1
99
        $delProfiles[] = sprintf('%s Custom Network', \core\CAT::$nomenclature_participant);
100
        $this->writeAdditionalDeletes($delProfiles);
101
        if ($setWired) {
102
            $this->loggerInstance->debug(4, "Saving LAN profile\n");
103
            $windowsProfile = $this->generateLANprofile();
104
            $this->saveProfile($windowsProfile);
105
        }
106
        $this->saveCerts();
107
        if ($this->selectedEap == \core\common\EAP::EAPTYPE_SILVERBULLET) {
108
            $this->writeClientP12File();
109
        }
110
        $this->copyFiles($this->selectedEap);
111
        $this->saveLogo();
112
        $this->writeMainNSH($this->selectedEap, $this->attributes);
113
        $this->compileNSIS();
114
        $installerPath = $this->signInstaller();
115
        \core\common\Entity::outOfThePotatoes();
116
        return $installerPath;
117
    }
118
    
119
    private function createProfileDir()
120
    {
121
        if (!is_dir('profiles')) {
122
            mkdir('profiles');
123
        }
124
    }
125
    
126
    /**
127
     * If separateHS20profiles is true then we should be saving OID and SSID
128
     * profiles separately. OID profiles should be considered optionl, i.e.
129
     * the installer should not report installation failure (??). If we decide
130
     * that such installation failures should be silent then it is enough if
131
     * these profiles are marked as hs20 and no "nohs" profiles are created
132
     */
133
    
134
    private function saveNetworkProfileSeparateHS($profileName, $network)
135
    {
136
        $out = '';
137
        if (!empty($network['ssid'])) {
138
            if ($this::separateSSIDprofiles === true && !empty($network['condition']) && $network['condition'] === 'locally_defined') {
139
                $out = "";
140
                foreach ($network['ssid'] as $ssid) {
141
                    $this->loggerInstance->debug(4, "SSID network: $ssid\n");
142
                    $windowsProfileSSID = $this->generateWlanProfile($ssid, [$ssid], 'WPA2', 'AES', [], false);
143
                    $this->saveProfile($windowsProfileSSID, $this->iterator, true);
144
                    $out .= "!insertmacro define_wlan_profile \"$ssid\" \"AES\" 0 \"0\"\n";
145
                    $this->iterator++;                     
146
                }
147
            } else {
148
                $this->loggerInstance->debug(4, "SSID network: $profileName\n");
149
                $windowsProfileSSID = $this->generateWlanProfile($profileName, $network['ssid'], 'WPA2', 'AES', [], false);
150
                $this->saveProfile($windowsProfileSSID, $this->iterator, true);
151
                $ssids = '';
152
                foreach ($network['ssid'] as $ssid) {
153
                    if ($ssid != $profileName) {
154
                        $ssids .= '|'.$ssid;
155
                    }
156
                }
157
                $out = "!insertmacro define_wlan_profile \"$profileName\" \"AES\" 0 \"$ssids\"\n";
158
                $this->iterator++;
159
            }
160
            $profileName .= " via partner";
161
        }
162
        if (!empty($network['oi'])) {
163
            $this->loggerInstance->debug(4, "RCOI network: $profileName\n");
164
            $windowsProfileHS = $this->generateWlanProfile($profileName, ['cat-passpoint-profile'], 'WPA2', 'AES', $network['oi'], true);
165
            $this->saveProfile($windowsProfileHS, $this->iterator, true);
166
            $out .= "!insertmacro define_wlan_profile \"$profileName\" \"AES\" 1 \"0\"\n";
167
            $this->iterator++;
168
        }
169
        return($out);
170
    }
171
    
172
    /**
173
     * If separateHS20profiles is false then we should be saving a hs20 profile
174
     * containing both OIDs and SSIDs. In addition we should also be saving
175
     * a nohs_... profile. When  the installer runs it first tries the normal
176
     * profile and if this fails it will try the nohs (if one exists)
177
     */
178
    
179
    private function saveNetworkProfileJoinedHS($profileName, $network)
180
    {
181
        $oiOnly = false;
182
        if ($network['ssid'] == []) {
183
            $oiOnly = true;
184
            $network['ssid'] = ['cat-passpoint-profile'];
185
        }
186
        $windowsProfile = $this->generateWlanProfile($profileName, $network['ssid'], 'WPA2', 'AES', $network['oi'], true);
187
        $this->saveProfile($windowsProfile, $this->iterator, true);
188
        if (!$oiOnly) {
189
            $windowsProfile = $this->generateWlanProfile($profileName, $network['ssid'], 'WPA2', 'AES', [], false);
190
            $this->saveProfile($windowsProfile, $this->iterator, false);
191
        }
192
        $this->iterator++;
193
        return("!insertmacro define_wlan_profile \"$profileName\" \"AES\" 2 \"".implode('|', $network['ssid'])."\"\n");
194
    }
195
196
    private function saveLogo()
197
    {
198
        $fedLogo = $this->attributes['fed:logo_file'] ?? NULL;
199
        $idpLogo = $this->attributes['internal:logo_file'] ?? NULL;
200
        $this->combineLogo($idpLogo, $fedLogo);
201
    }
202
203
    private function writeMainNSH($eap, $attr)
204
    {
205
        $this->loggerInstance->debug(4, "writeMainNSH");
206
        $this->loggerInstance->debug(4, $attr);
207
        $this->loggerInstance->debug(4, "Device_id = ".$this->device_id."\n");
208
        $fcontents = "!define W8\n";
209
        if ($this->device_id == 'w10') {
210
            $fcontents .= "!define W10\n";
211
        }
212
        $fcontents .= "Unicode true\n";
213
        if ($this->useGeantLink && $this->selectedEap['OUTER'] == \core\common\EAP::TTLS) {
214
            $eapStr = 'GEANTLink';
215
        } else {
216
            $eapStr = \core\common\EAP::eapDisplayName($this->selectedEap)['OUTER'];
217
        }
218
        if (isset($this->tlsOtherUsername) && $this->tlsOtherUsername == 1) {
219
            $fcontents .= "!define PFX_USERNAME\n";
220
        }
221
        if ($eap == \core\common\EAP::EAPTYPE_SILVERBULLET) {
222
            $fcontents .= "!define SILVERBULLET\n";
223
        }
224
        $fcontents .= '!define '.$eapStr;
225
        $fcontents .= "\n".'!define EXECLEVEL "user"';
226
        $fcontents .= $this->writeNsisDefines($attr);
227
        file_put_contents('main.nsh', $fcontents);
228
    }
229
230
    
231
    private function copyFiles($eap)
232
    {
233
        $this->loggerInstance->debug(4, "copyFiles start\n");
234
        $this->copyBasicFiles();
235
        switch ($eap["OUTER"]) {
236
            case \core\common\EAP::TTLS:
237
                if ($this->useGeantLink) {
238
                    $this->copyGeantLinkFiles();
239
                } else {
240
                    $this->copyStandardNsi();
241
                }
242
                break;
243
            default:
244
                $this->copyStandardNsi();
245
        }
246
        $this->loggerInstance->debug(4, "copyFiles end\n");
247
        return true;
248
    }
249
    
250
    private function copyStandardNsi()
251
    {
252
        if (!$this->translateFile('eap_w8.inc', 'cat.NSI')) {
253
            throw new Exception("Translating needed file eap_w8.inc failed!");
254
        }
255
    }
256
    
257
    private function saveCerts()
258
    {
259
        $caArray = $this->saveCertificateFiles('der');
260
        $fcontentsCerts = '';
261
        $fileHandleCerts = fopen('certs.nsh', 'w');
262
        if ($fileHandleCerts === false) {
263
            throw new Exception("Unable to open new certs.nsh file for writing CAs.");
264
        }
265
        foreach ($caArray as $certAuthority) {
266
            $store = $certAuthority['root'] ? "root" : "ca";
267
            $fcontentsCerts .= '!insertmacro install_ca_cert "'.$certAuthority['file'].'" "'.$certAuthority['sha1'].'" "'.$store."\"\n";
268
        }
269
        fwrite($fileHandleCerts, $fcontentsCerts);
270
        fclose($fileHandleCerts);
271
    }
272
273
    /* saveProvile writes a LAN or WLAN profile
274
     * @param string $profile the XML content to be saved
275
     * @param int $profileNumber the profile index or NULL to indicate a LAN profile
276
     * @param boolean $hs20 for WLAN profiles indicates if use the nohs prefix
277
     */
278
    private function saveProfile($profile, $profileNumber = NULL, $hs20 = false)
279
    {
280
        if ($hs20) {
281
            $prefix = 'w';
282
        } else {
283
            $prefix = 'nohs_w';
284
        }
285
        if (is_null($profileNumber)) {
286
            $prefix = '';
287
            $suffix = '';
288
        } else {
289
            $suffix = "-$profileNumber";
290
        }
291
        $xmlFname = "profiles/".$prefix."lan_prof".$suffix.".xml";
292
        $this->loggerInstance->debug(4, "Saving profile to ".$xmlFname."\n");
293
        file_put_contents($xmlFname, $profile);
294
    }
295
296
    /**
297
     * Selects the appropriate handler for a given EAP type and retirns
298
     * an initiated object
299
     * 
300
     * @return a profile object
0 ignored issues
show
Bug introduced by
The type devices\ms\a was not found. Did you mean a? If so, make sure to prefix the type with \.
Loading history...
301
     */
302
    
303
    private function setEapObject()
304
    {        
305
        switch ($this->selectedEap['OUTER']) {
306
            case \core\common\EAP::TTLS:
307
                if ($this->useGeantLink) {
308
                    return(new GeantLinkTtlsProfile());
0 ignored issues
show
Bug Best Practice introduced by
The expression return new devices\ms\GeantLinkTtlsProfile() returns the type devices\ms\GeantLinkTtlsProfile which is incompatible with the documented return type devices\ms\a.
Loading history...
309
                } else {
310
                    return(new MsTtlsProfile());
0 ignored issues
show
Bug Best Practice introduced by
The expression return new devices\ms\MsTtlsProfile() returns the type devices\ms\MsTtlsProfile which is incompatible with the documented return type devices\ms\a.
Loading history...
311
                }
312
            case \core\common\EAP::PEAP:
313
                return(new MsPeapProfile());
0 ignored issues
show
Bug Best Practice introduced by
The expression return new devices\ms\MsPeapProfile() returns the type devices\ms\MsPeapProfile which is incompatible with the documented return type devices\ms\a.
Loading history...
314
            case \core\common\EAP::TLS:
315
                return(new MsTlsProfile());
0 ignored issues
show
Bug Best Practice introduced by
The expression return new devices\ms\MsTlsProfile() returns the type devices\ms\MsTlsProfile which is incompatible with the documented return type devices\ms\a.
Loading history...
316
            default:
317
                // use Exception here
318
                break;
319
        }
320
    }
321
    
322
    private function setupEapConfig() {
323
        $servers = empty($this->attributes['eap:server_name']) ? '' : implode(';', $this->attributes['eap:server_name']);
324
        $outerId = $this->determineOuterIdString();
325
        $nea = (\core\common\Entity::getAttributeValue($this->attributes, 'media:wired', 0) === 'on') ? 'true' : 'false';
326
        $otherTlsName = \core\common\Entity::getAttributeValue($this->attributes, 'eap-specific:tls_use_other_id', 0) === 'on' ? 'true' : 'false';
327
        if (isset(\core\common\Entity::getAttributeValue($this->attributes, 'device-specific:geantlink', $this->device_id)[0]) &&
328
             \core\common\Entity::getAttributeValue($this->attributes, 'device-specific:geantlink', $this->device_id)[0] === 'on') {
329
             $this->useGeantLink = true;
330
        } else { 
331
             $this->useGeantLink = false;
332
        }
333
        $eapConfig = $this->setEapObject();
334
        $eapConfig->setInnerType($this->selectedEap['INNER']);
335
        $eapConfig->setInnerTypeDisplay(\core\common\EAP::eapDisplayName($this->selectedEap)['INNER']);
336
        $eapConfig->setCAList($this->getAttribute('internal:CAs')[0]);
337
        $eapConfig->setServerNames($servers);
338
        $eapConfig->setOuterId($outerId);
339
        $eapConfig->setNea($nea);
340
        $eapConfig->setDisplayName($this->translateString($this->attributes['general:instname'][0]));
341
        $eapConfig->setIdPId($this->deviceUUID);
342
        $eapConfig->setOtherTlsName($otherTlsName);
343
        $eapConfig->setConfig();
344
        $this->eapConfigObject = $eapConfig;
345
    } 
346
        
347
    private function generateWlanProfile($networkName, $ssids, $authentication, $encryption, $ois, $hs20 = false)
348
    {
349
        if (empty($this->attributes['internal:realm'][0])) {
350
            $domainName = \config\ConfAssistant::CONSORTIUM['interworking-domainname-fallback'];
351
        } else {
352
            $domainName = $this->attributes['internal:realm'][0];
353
        }
354
        $wlanProfile = new MsWlanProfile();
355
        $wlanProfile->setName($networkName);       
356
        $wlanProfile->setEncryption($authentication, $encryption);
357
        $wlanProfile->setSSIDs($ssids);
358
        $wlanProfile->setHS20($hs20);
359
        $wlanProfile->setOIs($ois);
360
        $wlanProfile->setDomainName($domainName);
361
        $wlanProfile->setEapConfig($this->eapConfigObject);
362
        return($wlanProfile->writeWLANprofile());
363
    }
364
    
365
    private function generateLanProfile()
366
    {
367
        $lanProfile = new MsLanProfile();
368
        $lanProfile->setEapConfig($this->eapConfigObject);
369
        return($lanProfile->writeLANprofile());
370
    }
371
372
    private $eapTypeId;
0 ignored issues
show
introduced by
The private property $eapTypeId is not used, and could be removed.
Loading history...
373
    private $eapAuthorId;
0 ignored issues
show
introduced by
The private property $eapAuthorId is not used, and could be removed.
Loading history...
374
    private $eapConfigObject;
375
    private $profileNames;
376
    private $iterator;
377
}
378
379
380
381