Passed
Push — release_2_1 ( cd62b4...574b22 )
by Tomasz
09:12
created

DeviceW8W10::copyStandardNsi()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 2
nc 2
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 requres 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\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
                $out = "!insertmacro define_wlan_profile \"$profileName\" \"AES\" 0\n";
152
                $this->iterator++;
153
            }
154
            $profileName .= " via partner";
155
        }
156
        if (!empty($network['oi'])) {
157
            $this->loggerInstance->debug(4, "RCOI network: $profileName\n");
158
            $windowsProfileHS = $this->generateWlanProfile($profileName, ['cat-passpoint-profile'], 'WPA2', 'AES', $network['oi'], true);
159
            $this->saveProfile($windowsProfileHS, $this->iterator, true);
160
            $out .= "!insertmacro define_wlan_profile \"$profileName\" \"AES\" 1\n";
161
            $this->iterator++;
162
        }
163
        return($out);
164
    }
165
    
166
    /**
167
     * If separateHS20profiles is false then we should be saving a hs20 profile
168
     * containing both OIDs and SSIDs. In addiotion we should also be saving
169
     * a nohs_... profile. When  the installer runs it first tries the normal
170
     * profile and if this fails it will try the nohs (if one exists)
171
     */
172
    
173
    private function saveNetworkProfileJoinedHS($profileName, $network)
174
    {
175
        $oiOnly = false;
176
        if ($network['ssid'] == []) {
177
            $oiOnly = true;
178
            $network['ssid'] = ['cat-passpoint-profile'];
179
        }
180
        $windowsProfile = $this->generateWlanProfile($profileName, $network['ssid'], 'WPA2', 'AES', $network['oi'], true);
181
        $this->saveProfile($windowsProfile, $this->iterator, true);
182
        if (!$oiOnly) {
183
            $windowsProfile = $this->generateWlanProfile($profileName, $network['ssid'], 'WPA2', 'AES', [], false);
184
            $this->saveProfile($windowsProfile, $this->iterator, false);
185
        }
186
        $this->iterator++;
187
        return("!insertmacro define_wlan_profile \"$profileName\" \"AES\" 2\n");
188
    }
189
190
    private function saveLogo()
191
    {
192
        $fedLogo = $this->attributes['fed:logo_file'] ?? NULL;
193
        $idpLogo = $this->attributes['internal:logo_file'] ?? NULL;
194
        $this->combineLogo($idpLogo, $fedLogo);
195
    }
196
197
    private function writeMainNSH($eap, $attr)
198
    {
199
        $this->loggerInstance->debug(4, "writeMainNSH");
200
        $this->loggerInstance->debug(4, $attr);
201
        $this->loggerInstance->debug(4, "Device_id = ".$this->device_id."\n");
202
        $fcontents = "!define W8\n";
203
        if ($this->device_id == 'w10') {
204
            $fcontents .= "!define W10\n";
205
        }
206
        $fcontents .= "Unicode true\n";
207
        if ($this->useGeantLink) {
208
            $eapStr = 'GEANTLink';
209
        } else {
210
            $eapStr = \core\common\EAP::eapDisplayName($this->selectedEap)['OUTER'];
211
        }
212
        if (isset($this->tlsOtherUsername) && $this->tlsOtherUsername == 1) {
213
            $fcontents .= "!define PFX_USERNAME\n";
214
        }
215
        if ($eap == \core\common\EAP::EAPTYPE_SILVERBULLET) {
216
            $fcontents .= "!define SILVERBULLET\n";
217
        }
218
        $fcontents .= '!define '.$eapStr;
219
        $fcontents .= "\n".'!define EXECLEVEL "user"';
220
        $fcontents .= $this->writeNsisDefines($attr);
221
        file_put_contents('main.nsh', $fcontents);
222
    }
223
224
    
225
    private function copyFiles($eap)
226
    {
227
        $this->loggerInstance->debug(4, "copyFiles start\n");
228
        $this->copyBasicFiles();
229
        switch ($eap["OUTER"]) {
230
            case \core\common\EAP::TTLS:
231
                if ($this->useGeantLink) {
232
                    $this->copyGeantLinkFiles();
233
                } else {
234
                    $this->copyStandardNsi();
235
                }
236
                break;
237
            default:
238
                $this->copyStandardNsi();
239
        }
240
        $this->loggerInstance->debug(4, "copyFiles end\n");
241
        return true;
242
    }
243
    
244
    private function copyStandardNsi()
245
    {
246
        if (!$this->translateFile('eap_w8.inc', 'cat.NSI')) {
247
            throw new Exception("Translating needed file eap_w8.inc failed!");
248
        }
249
    }
250
    
251
    private function saveCerts()
252
    {
253
        $caArray = $this->saveCertificateFiles('der');
254
        $fcontentsCerts = '';
255
        $fileHandleCerts = fopen('certs.nsh', 'w');
256
        if ($fileHandleCerts === false) {
257
            throw new Exception("Unable to open new certs.nsh file for writing CAs.");
258
        }
259
        foreach ($caArray as $certAuthority) {
260
            $store = $certAuthority['root'] ? "root" : "ca";
261
            $fcontentsCerts .= '!insertmacro install_ca_cert "'.$certAuthority['file'].'" "'.$certAuthority['sha1'].'" "'.$store."\"\n";
262
        }
263
        fwrite($fileHandleCerts, $fcontentsCerts);
264
        fclose($fileHandleCerts);
265
    }
266
267
    /* saveProvile writes a LAN or WLAN profile
268
     * @param string $profile the XML content to be saved
269
     * @param int $profileNumber the profile index or NULL to indicate a LAN profile
270
     * @param boolean $hs20 for WLAN profiles indicates if use the nohs prefix
271
     */
272
    private function saveProfile($profile, $profileNumber = NULL, $hs20 = false)
273
    {
274
        if ($hs20) {
275
            $prefix = 'w';
276
        } else {
277
            $prefix = 'nohs_w';
278
        }
279
        if (is_null($profileNumber)) {
280
            $prefix = '';
281
            $suffix = '';
282
        } else {
283
            $suffix = "-$profileNumber";
284
        }
285
        $xmlFname = "profiles/".$prefix."lan_prof".$suffix.".xml";
286
        $this->loggerInstance->debug(4, "Saving profile to ".$xmlFname."\n");
287
        file_put_contents($xmlFname, $profile);
288
    }
289
290
    /**
291
     * Selects the approprate handler for a given EAP type and retirns
292
     * an initiated object
293
     * 
294
     * @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...
295
     */
296
    
297
    private function setEapObject()
298
    {        
299
        switch ($this->selectedEap['OUTER']) {
300
            case \core\common\EAP::TTLS:
301
                if ($this->useGeantLink) {
302
                    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...
303
                } else {
304
                    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...
305
                }
306
            case \core\common\EAP::PEAP:
307
                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...
308
            case \core\common\EAP::TLS:
309
                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...
310
            default:
311
                // use Exception here
312
                break;
313
        }
314
    }
315
    
316
    private function setupEapConfig() {
317
        $servers = empty($this->attributes['eap:server_name']) ? '' : implode(';', $this->attributes['eap:server_name']);
318
        $outerId = $this->determineOuterIdString();
319
        $nea = (\core\common\Entity::getAttributeValue($this->attributes, 'media:wired', 0) === 'on') ? 'true' : 'false';
320
        $otherTlsName = \core\common\Entity::getAttributeValue($this->attributes, 'eap-specific:tls_use_other_id', 0) === 'on' ? 'true' : 'false';
321
        $this->useGeantLink = \core\common\Entity::getAttributeValue($this->attributes, 'device-specific:geantlink', $this->device_id) === 'on' ? true : false;
322
        $eapConfig = $this->setEapObject();
323
        $eapConfig->setInnerType($this->selectedEap['INNER']);
324
        $eapConfig->setInnerTypeDisplay(\core\common\EAP::eapDisplayName($this->selectedEap)['INNER']);
325
        $eapConfig->setCAList($this->getAttribute('internal:CAs')[0]);
326
        $eapConfig->setServerNames($servers);
327
        $eapConfig->setOuterId($outerId);
328
        $eapConfig->setNea($nea);
329
        $eapConfig->setDisplayName($this->translateString($this->attributes['general:instname'][0]));
330
        $eapConfig->setIdPId($this->deviceUUID);
331
        $eapConfig->setOtherTlsName($otherTlsName);
332
        $eapConfig->setConfig();
333
        $this->eapConfigObject = $eapConfig;
334
    } 
335
        
336
    private function generateWlanProfile($networkName, $ssids, $authentication, $encryption, $ois, $hs20 = false)
337
    {
338
        if (empty($this->attributes['internal:realm'][0])) {
339
            $domainName = \config\ConfAssistant::CONSORTIUM['interworking-domainname-fallback'];
340
        } else {
341
            $domainName = $this->attributes['internal:realm'][0];
342
        }
343
        $wlanProfile = new MsWlanProfile();
344
        $wlanProfile->setName($networkName);       
345
        $wlanProfile->setEncryption($authentication, $encryption);
346
        $wlanProfile->setSSIDs($ssids);
347
        $wlanProfile->setHS20($hs20);
348
        $wlanProfile->setOIs($ois);
349
        $wlanProfile->setDomainName($domainName);
350
        $wlanProfile->setEapConfig($this->eapConfigObject);
351
        return($wlanProfile->writeWLANprofile());
352
    }
353
    
354
    private function generateLanProfile()
355
    {
356
        $lanProfile = new MsLanProfile();
357
        $lanProfile->setEapConfig($this->eapConfigObject);
358
        return($lanProfile->writeLANprofile());
359
    }
360
361
    private $eapTypeId;
0 ignored issues
show
introduced by
The private property $eapTypeId is not used, and could be removed.
Loading history...
362
    private $eapAuthorId;
0 ignored issues
show
introduced by
The private property $eapAuthorId is not used, and could be removed.
Loading history...
363
    private $eapConfigObject;
364
    private $profileNames;
365
    private $iterator;
366
}
367
368
369
370