Passed
Branch release_2_0 (b77d1e)
by Stefan
07:41
created

MobileconfigSuperclass   F

Complexity

Total Complexity 60

Size/Duplication

Total Lines 675
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 1
Metric Value
wmc 60
eloc 293
c 7
b 0
f 1
dl 0
loc 675
rs 3.6

17 Methods

Rating   Name   Duplication   Size   Complexity  
A proxySettings() 0 21 2
A generalPayload() 0 28 3
A clientP12Block() 0 36 2
A expiryBlock() 0 8 2
B allNetworkBlocks() 0 24 9
A passPointBlock() 0 43 2
A writeInstaller() 0 66 5
A allCA() 0 9 2
A consentBlock() 0 18 4
C networkBlock() 0 85 11
A __construct() 0 8 1
A writeDeviceInfo() 0 24 3
B eapBlock() 0 51 7
A massageName() 0 3 1
A listCAUuids() 0 7 2
A removenetworkBlock() 0 32 1
A caBlob() 0 41 3

How to fix   Complexity   

Complex Class

Complex classes like MobileconfigSuperclass often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MobileconfigSuperclass, and based on these observations, apply Extract Interface, too.

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 contains the installer for iOS devices and Apple 10.7 Lion
25
 *
26
 *
27
 * @author Stefan Winter <[email protected]>
28
 * @package Developer
29
 */
30
31
namespace devices\apple_mobileconfig;
32
33
use \Exception;
34
35
/**
36
 * This is the main implementation class of the module
37
 *
38
 * The class should only define one public method: writeInstaller.
39
 *
40
 * All other methods and properties should be private. This example sets zipInstaller method to protected, so that it can be seen in the documentation.
41
 *
42
 * @package Developer
43
 */
44
abstract class MobileconfigSuperclass extends \core\DeviceConfig
45
{
46
47
    private $instName;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
48
    private $profileName;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
49
    private $massagedInst;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
50
    private $massagedProfile;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
51
    private $massagedCountry;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
52
    private $massagedConsortium;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
53
    private $lang;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
54
    static private $iPhonePayloadPrefix = "org.1x-config";
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
55
    private $clientCertUUID;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
56
57
    /**
58
     * construct with the standard set of EAP methods we support, and preload
59
     * specialities
60
     */
61
    public function __construct()
62
    {
63
        parent::__construct();
64
        \core\common\Entity::intoThePotatoes();
65
        // that's what all variants support. Sub-classes can change it.
66
        $this->setSupportedEapMethods([\core\common\EAP::EAPTYPE_PEAP_MSCHAP2, \core\common\EAP::EAPTYPE_TTLS_PAP, \core\common\EAP::EAPTYPE_TTLS_MSCHAP2, \core\common\EAP::EAPTYPE_SILVERBULLET]);
67
        $this->specialities['internal:verify_userinput_suffix'] = _("It is not possible to actively verify the user input for suffix match; but if there is no 'Terms of Use' configured, the installer will display a corresponding hint to the user instead.");
68
        \core\common\Entity::outOfThePotatoes();
69
    }
70
71
    /**
72
     * massage a name so that it becomes acceptable inside the plist XML
73
     * 
74
     * @param string $input the literal name
75
     * @return string
76
     */
77
    private function massageName($input)
78
    {
79
        return htmlspecialchars(strtolower(iconv("UTF-8", "US-ASCII//TRANSLIT", preg_replace(['/ /', '/\//'], '_', $input))), ENT_XML1, 'UTF-8');
80
    }
81
82
    /**
83
     * the general part of a mobileconfig file in plist format
84
     * @return string
85
     */
86
    private function generalPayload()
87
    {
88
        \core\common\Entity::intoThePotatoes();
89
        $tagline = sprintf(_("Network configuration profile '%s' of '%s' - provided by %s"), htmlspecialchars($this->profileName, ENT_XML1, 'UTF-8'), htmlspecialchars($this->instName, ENT_XML1, 'UTF-8'), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
90
91
        $eapType = $this->selectedEap;
92
        // simpler message for silverbullet
93
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
94
            $tagline = sprintf(_("%s configuration for IdP '%s' - provided by %s"), \core\ProfileSilverbullet::PRODUCTNAME, htmlspecialchars($this->instName, ENT_XML1, 'UTF-8'), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
95
        }
96
97
        $retval = "
98
      <key>PayloadDescription</key>
99
         <string>$tagline</string>
100
      <key>PayloadDisplayName</key>
101
         <string>" . CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'] . "</string>
102
      <key>PayloadIdentifier</key>
103
         <string>" . self::$iPhonePayloadPrefix . ".$this->massagedConsortium.$this->massagedCountry.$this->massagedInst.$this->massagedProfile.$this->lang</string>
104
      <key>PayloadOrganization</key>
105
         <string>" . htmlspecialchars(iconv("UTF-8", "UTF-8//IGNORE", $this->attributes['general:instname'][0]), ENT_XML1, 'UTF-8') . ( $this->attributes['internal:profile_count'][0] > 1 ? " (" . htmlspecialchars(iconv("UTF-8", "UTF-8//IGNORE", $this->attributes['profile:name'][0]), ENT_XML1, 'UTF-8') . ")" : "") . "</string>
106
      <key>PayloadType</key>
107
         <string>Configuration</string>
108
      <key>PayloadUUID</key>
109
         <string>" . \core\common\Entity::uuid('', self::$iPhonePayloadPrefix . $this->massagedConsortium . $this->massagedCountry . $this->massagedInst . $this->massagedProfile) . "</string>
110
      <key>PayloadVersion</key>
111
         <integer>1</integer>";
112
        \core\common\Entity::outOfThePotatoes();
113
        return $retval;
114
    }
115
116
    const FILE_START = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
117
<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\"
118
\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
119
<plist version=\"1.0\">
120
<dict>";
121
    const FILE_END = "</dict></plist>";
122
    const BUFFER_CONSENT_PRE = "
123
      <key>ConsentText</key>
124
         <dict>
125
            <key>default</key>
126
               <string>";
127
    const BUFFER_CONSENT_POST = "</string>
128
         </dict>
129
         ";
130
131
    /**
132
     * creates a ConsentText block if either Terms of Use are specified or the
133
     * user input hints should be displayed. Otherwise, produces nothing.
134
     * 
135
     * @return string
136
     */
137
    protected function consentBlock()
138
    {
139
        \core\common\Entity::intoThePotatoes();
140
        if (isset($this->attributes['support:info_file'])) {
141
            return MobileconfigSuperclass::BUFFER_CONSENT_PRE . htmlspecialchars(iconv("UTF-8", "UTF-8//TRANSLIT", $this->attributes['support:info_file'][0]), ENT_XML1, 'UTF-8') . MobileconfigSuperclass::BUFFER_CONSENT_POST;
142
        }
143
        if ($this->attributes['internal:verify_userinput_suffix'][0] != 0) {
144
            if (strlen($this->attributes['internal:realm'][0]) > 0) {
145
                $retval = MobileconfigSuperclass::BUFFER_CONSENT_PRE . sprintf(_("Important Notice: your username must end with @%s!"), $this->attributes['internal:realm'][0]) . MobileconfigSuperclass::BUFFER_CONSENT_POST;
146
                \core\common\Entity::outOfThePotatoes();
147
                return $retval;
148
            }
149
            $retval = MobileconfigSuperclass::BUFFER_CONSENT_PRE . _("Important Notice: your username MUST be in the form of xxx@yyy where the yyy is a common suffix identifying your Identity Provider. Please find out what to use there and enter the username in the correct format.") . MobileconfigSuperclass::BUFFER_CONSENT_POST;
150
            \core\common\Entity::outOfThePotatoes();
151
            return $retval;
152
        }
153
        \core\common\Entity::outOfThePotatoes();
154
        return "";
155
    }
156
157
    /**
158
     * create the actual installer XML file
159
     * 
160
     * @return string filename of the generated installer
161
     *
162
     */
163
    public function writeInstaller()
164
    {
165
        \core\common\Entity::intoThePotatoes();
166
167
        $this->loggerInstance->debug(4, "mobileconfig Module Installer start\n");
168
169
        // remove spaces and slashes (filename!), make sure it's simple ASCII only, then lowercase it
170
        // also escape htmlspecialchars
171
        // not all names and profiles have a name, so be prepared
172
173
        $this->loggerInstance->debug(5, "List of available attributes: " . var_export($this->attributes, TRUE));
174
175
        $this->instName = $this->attributes['general:instname'][0] ?? _("Unnamed Organisation");
176
        $this->profileName = $this->attributes['profile:name'][0] ?? _("Unnamed Profile");
177
178
        $this->massagedInst = $this->massageName($this->instName);
179
        $this->massagedProfile = $this->massageName($this->profileName);
180
        $this->massagedCountry = $this->massageName($this->attributes['internal:country'][0]);
181
        $this->massagedConsortium = $this->massageName(CONFIG_CONFASSISTANT['CONSORTIUM']['name']);
182
        $this->lang = preg_replace('/\..+/', '', setlocale(LC_ALL, "0"));
183
184
        $eapType = $this->selectedEap;
185
186
        $outputXml = self::FILE_START;
187
        $outputXml .= "<key>PayloadContent</key>
188
         <array>";
189
190
        // if we are in silverbullet, we will need a whole own block for the client credential
191
        // and also for the profile expiry
192
193
        $this->clientCertUUID = NULL;
194
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
195
            $blockinfo = $this->clientP12Block();
196
            $outputXml .= $blockinfo['block'];
197
            $this->clientCertUUID = $blockinfo['UUID'];
198
        }
199
200
        $outputXml .= $this->allCA();
201
202
        $outputXml .= $this->allNetworkBlocks();
203
204
        $outputXml .= "</array>";
205
        $outputXml .= $this->generalPayload();
206
        $outputXml .= $this->consentBlock();
207
208
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
209
            $outputXml .= $this->expiryBlock();
210
        }
211
        $outputXml .= self::FILE_END;
212
213
        file_put_contents('installer_profile', $outputXml);
214
215
        $fileName = $this->installerBasename . '.mobileconfig';
216
217
        if (!$this->sign) {
218
            rename("installer_profile", $fileName);
219
            \core\common\Entity::outOfThePotatoes();
220
            return $fileName;
221
        }
222
        // still here? Then we are signing.
223
        $signing = system($this->sign . " installer_profile '$fileName' > /dev/null");
224
        if ($signing === FALSE) {
225
            $this->loggerInstance->debug(2, "Signing the mobileconfig installer $fileName FAILED!\n");
226
        }
227
        \core\common\Entity::outOfThePotatoes();
228
        return $fileName;
229
    }
230
231
    /**
232
     * produces the HTML text to be displayed when clicking on the "help" button
233
     * besides the download button.
234
     * 
235
     * @return string
236
     */
237
    public function writeDeviceInfo()
238
    {
239
        \core\common\Entity::intoThePotatoes();
240
        $ssidCount = count($this->attributes['internal:SSID']);
241
        $certCount = count($this->attributes['internal:CAs'][0]);
242
        $out = "<p>" . _("For best results, please use the built-in browser (Safari) to open the configuration file.") . "</p>";
243
        $out .= "<p>";
244
        $out .= _("The profile will install itself after you click (or tap) the button. You will be asked for confirmation/input at several points:");
245
        $out .= "<ul>";
246
        $out .= "<li>" . _("to install the profile") . "</li>";
247
        $out .= "<li>" . ngettext("to accept the server certificate authority", "to accept the server certificate authorities", $certCount);
248
        if ($certCount > 1) {
249
            $out .= " " . sprintf(_("(%d times)"), $certCount);
250
        }
251
        $out .= "</li>";
252
        $out .= "<li>" . _("to enter the username and password you have been given by your organisation");
253
        if ($ssidCount > 1) {
254
            $out .= " " . sprintf(_("(%d times each, because %s is installed for %d SSIDs)"), $ssidCount, CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'], $ssidCount);
255
        }
256
        $out .= "</li>";
257
        $out .= "</ul>";
258
        $out .= "</p>";
259
        \core\common\Entity::outOfThePotatoes();
260
        return $out;
261
    }
262
263
    /**
264
     * collates a list of the UUIDs of all the CAs which are to be included in
265
     * the mobileconfig file
266
     * 
267
     * @return array
268
     */
269
    private function listCAUuids()
270
    {
271
        $retval = [];
272
        foreach ($this->attributes['internal:CAs'][0] as $ca) {
273
            $retval[] = $ca['uuid'];
274
        }
275
        return $retval;
276
    }
277
278
    /**
279
     * This is the XML structure subtree of a Network block which contains the
280
     * settings specific to Passpoint
281
     * 
282
     * @param string $consortiumOi list of consortiumOi to put into structure
283
     * @param string $oiName       the pretty-print name of the RCOI
284
     * @return string
285
     */
286
    private function passPointBlock($consortiumOi, $oiName)
287
    {
288
        $retval = "
289
               <key>IsHotspot</key>
290
               <true/>
291
               <key>ServiceProviderRoamingEnabled</key>
292
               <true/>
293
               <key>DisplayedOperatorName</key>
294
               <string>";
295
        $retval .= "$oiName</string>";
296
        // if we don't know the realm, omit the entire DomainName key
297
        if (isset($this->attributes['internal:realm'])) {
298
            $retval .= "<key>DomainName</key>
299
               <string>";
300
            $retval .= $this->attributes['internal:realm'][0];
301
            $retval .= "</string>
302
                ";
303
        }
304
        $retval .= "                <key>RoamingConsortiumOIs</key>
305
                <array>";
306
307
        $retval .= "<string>" . strtoupper($consortiumOi) . "</string>";
308
309
        $retval .= "</array>";
310
        // this is an undocumented value found on the net. Does it do something useful?
311
        $retval .= "<key>_UsingHotspot20</key>
312
                <true/>
313
                ";
314
        // do we need to set NAIRealmName ? In Rel 1, probably yes, in Rel 2, 
315
        // no because ConsortiumOI is enough.
316
        // but which release is OS X doing? And what should we fill in, given
317
        // that we have thousands of realms? Try just eduroam.org
318
        // 
319
        // tests from Hideaki suggest it's better not to set it; if Roaming
320
        // consortium OI and NAIRealmNames are both set, connecting to a hotspot
321
        // with just RCOI does not work
322
        /* if (CONFIG_CONFASSISTANT['CONSORTIUM']['name'] == "eduroam") {
323
          $retval .= "<key>NAIRealmNames</key>
324
          <array>
325
          <string>eduroam.org</string>
326
          </array>";
327
          } */
328
        return $retval;
329
    }
330
331
    private $serial;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
332
    private $removeSerial;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
333
    private $caSerial;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
334
335
    /**
336
     * produces the EAP sub-block of a Network block
337
     * 
338
     * @param array $eapType EAP type in array notation
339
     * @return string
340
     */
341
    private function eapBlock($eapType)
342
    {
343
        $realm = $this->determineOuterIdString();
344
        $retval = "<key>EAPClientConfiguration</key>
345
                  <dict>
346
                      <key>AcceptEAPTypes</key>
347
                         <array>
348
                            <integer>" . $eapType['OUTER'] . "</integer>
349
                         </array>
350
                      <key>EAPFASTProvisionPAC</key>
351
                            <true />
352
                      <key>EAPFASTUsePAC</key>
353
                            <true />
354
                      <key>EAPFastProvisionPACAnonymously</key>
355
                            <false />
356
                      <key>OneTimeUserPassword</key>
357
                            <false />
358
";
359
        if ($realm !== NULL) {
360
            $retval .= "<key>OuterIdentity</key>
361
                                    <string>" . htmlspecialchars($realm, ENT_XML1, 'UTF-8') . "</string>
362
";
363
        }
364
        $retval .= "<key>PayloadCertificateAnchorUUID</key>
365
                         <array>";
366
        foreach ($this->listCAUuids() as $uuid) {
367
            if (in_array($uuid, $this->CAsAccountedFor)) {
368
                $retval .= "
369
<string>$uuid</string>";
370
            }
371
        }
372
        $retval .= "
373
                         </array>
374
                      <key>TLSAllowTrustExceptions</key>
375
                         <false />
376
                      <key>TLSTrustedServerNames</key>
377
                         <array>";
378
        foreach ($this->attributes['eap:server_name'] as $commonName) {
379
            $retval .= "
380
<string>$commonName</string>";
381
        }
382
        $retval .= "
383
                         </array>";
384
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
385
            $retval .= "<key>UserName</key><string>" . $this->clientCert["certObject"]->username . "</string>";
386
        }
387
        $retval .= "
388
                      <key>TTLSInnerAuthentication</key>
389
                         <string>" . ($eapType['INNER'] == \core\common\EAP::NONE ? "PAP" : "MSCHAPv2") . "</string>
390
                   </dict>";
391
        return $retval;
392
    }
393
394
    /**
395
     * produces the Proxy sub-block of a Network block
396
     * 
397
     * @return string
398
     */
399
    protected function proxySettings()
400
    {
401
        $buffer = "<key>ProxyType</key>";
402
        if (isset($this->attributes['media:force_proxy'])) {
403
            // find the port delimiter. In case of IPv6, there are multiple ':' 
404
            // characters, so we have to find the LAST one
405
            $serverAndPort = explode(':', strrev($this->attributes['media:force_proxy'][0]), 2);
406
            // characters are still reversed, invert on use!
407
            $buffer .= "<string>Manual</string>
408
                  <key>ProxyServer</key>
409
                  <string>" . strrev($serverAndPort[1]) . "</string>
410
                  <key>ProxyServerPort</key>
411
                  <integer>" . strrev($serverAndPort[0]) . "</integer>
412
                  <key>ProxyPACFallbackAllowed</key>
413
                  <false/>";
414
        } else {
415
            $buffer .= "<string>Auto</string>
416
                  <key>ProxyPACFallbackAllowed</key>
417
                  <true/>";
418
        }
419
        return $buffer;
420
    }
421
422
    /**
423
     * produces an entire Network block
424
     * 
425
     * @param int                  $blocktype      which type of network block is this?
426
     * @param string|array|boolean $toBeConfigured variable part of the config. Single SSID or list of ConsortiumOi
427
     * @return string
428
     * @throws Exception
429
     */
430
    private function networkBlock($blocktype, $toBeConfigured)
431
    {
432
        \core\common\Entity::intoThePotatoes();
433
        $eapType = $this->selectedEap;
434
        switch ($blocktype) {
435
            case MobileconfigSuperclass::NETWORK_BLOCK_TYPE_SSID:
436
                if (!is_string($toBeConfigured)) {
437
                    throw new Exception("SSID must be a string!");
438
                }
439
                $escapedSSID = htmlspecialchars($toBeConfigured, ENT_XML1, 'UTF-8');
440
                $payloadIdentifier = "wifi." . $this->serial;
441
                $payloadShortName = sprintf(_("SSID %s"), $escapedSSID);
442
                $payloadName = sprintf(_("%s configuration for network name %s"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'], $escapedSSID);
443
                $encryptionTypeString = "WPA";
444
                $setupModesString = "";
445
                $wifiNetworkIdentification = "<key>SSID_STR</key>
446
                  <string>$escapedSSID</string>";
447
                break;
448
            case MobileconfigSuperclass::NETWORK_BLOCK_TYPE_WIRED:
449
                if (!is_bool($toBeConfigured)) {
450
                    throw new Exception("We expected a TRUE here!");
451
                }
452
                $payloadIdentifier = "firstactiveethernet";
453
                $payloadShortName = _("Wired Network");
454
                $payloadName = sprintf(_("%s configuration for wired network"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
455
                $encryptionTypeString = "any";
456
                $setupModesString = "
457
               <key>SetupModes</key>
458
                  <array>
459
                     <string>System</string>
460
                  </array>";
461
                $wifiNetworkIdentification = "";
462
                break;
463
            case MobileconfigSuperclass::NETWORK_BLOCK_TYPE_CONSORTIUMOIS:
464
                if (!is_string($toBeConfigured)) {
465
                    throw new Exception("ConsortiumOI must be a string!");
466
                }
467
                $payloadIdentifier = "hs20.$toBeConfigured";
468
                $knownOiName = array_search($toBeConfigured, CONFIG_CONFASSISTANT['CONSORTIUM']['interworking-consortium-oi']);
469
                if ($knownOiName === FALSE) { // a custom RCOI as set by the IdP admin; do not use the term "eduroam" in that one!
470
                    $knownOiName = $this->instName . " "._("Roaming Partner");
471
                }
472
                $payloadShortName = $knownOiName;
473
                $payloadName = _("Passpoint roaming configuration ($knownOiName)");
474
                $encryptionTypeString = "WPA";
475
                $setupModesString = "";
476
                $wifiNetworkIdentification = $this->passPointBlock($toBeConfigured, $knownOiName);
477
                break;
478
            default:
479
                throw new Exception("This type of network block is unknown!");
480
        }
481
        $retval = "<dict>";
482
        $retval .= $this->eapBlock($eapType);
483
        $retval .= "<key>EncryptionType</key>
484
                  <string>$encryptionTypeString</string>
485
               <key>HIDDEN_NETWORK</key>
486
                  <true />
487
               <key>PayloadDescription</key>
488
                  <string>$payloadName</string>
489
               <key>PayloadDisplayName</key>
490
                  <string>$payloadShortName</string>
491
               <key>PayloadIdentifier</key>
492
                  <string>" . self::$iPhonePayloadPrefix . ".$this->massagedConsortium.$this->massagedCountry.$this->massagedInst.$this->massagedProfile.$this->lang.$payloadIdentifier</string>
493
               <key>PayloadOrganization</key>
494
                  <string>" . $this->massagedConsortium . ".1x-config.org</string>
495
               <key>PayloadType</key>
496
                  <string>com.apple." . ($blocktype == MobileconfigSuperclass::NETWORK_BLOCK_TYPE_WIRED ? "firstactiveethernet" : "wifi") . ".managed</string>";
497
        $retval .= $this->proxySettings();
498
        $retval .= $setupModesString;
499
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
500
            if ($this->clientCertUUID === NULL) {
501
                throw new Exception("Silverbullet REQUIRES a client certificate and we need to know the UUID!");
502
            }
503
            $retval .= "<key>PayloadCertificateUUID</key>
504
                        <string>$this->clientCertUUID</string>";
505
        }
506
        $retval .= "
507
               <key>PayloadUUID</key>
508
                  <string>" . \core\common\Entity::uuid() . "</string>
509
               <key>PayloadVersion</key>
510
                  <integer>1</integer>
511
                  $wifiNetworkIdentification</dict>";
512
        $this->serial = $this->serial + 1;
513
        \core\common\Entity::outOfThePotatoes();
514
        return $retval;
515
    }
516
517
    /**
518
     * Produces a Network block which sets a network to manual join (we don't
519
     * get any closer to removing a network in mobileconfig)
520
     * 
521
     * @param string $ssid the SSID to set to manual join only
522
     * @return string
523
     */
524
    private function removenetworkBlock($ssid)
525
    {
526
        \core\common\Entity::intoThePotatoes();
527
        $retval = "
528
<dict>
529
	<key>AutoJoin</key>
530
	<false/>
531
	<key>EncryptionType</key>
532
	<string>None</string>
533
	<key>HIDDEN_NETWORK</key>
534
	<false/>
535
	<key>IsHotspot</key>
536
	<false/>
537
	<key>PayloadDescription</key>
538
	<string>" . sprintf(_("This SSID should not be used after bootstrapping %s"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']) . "</string>
539
	<key>PayloadDisplayName</key>
540
	<string>" . _("Disabled WiFi network") . "</string>
541
	<key>PayloadIdentifier</key>
542
	<string>" . self::$iPhonePayloadPrefix . ".$this->massagedConsortium.$this->massagedCountry.$this->massagedInst.$this->massagedProfile.$this->lang.wifi.disabled.$this->removeSerial</string>
543
	<key>PayloadType</key>
544
	<string>com.apple.wifi.managed</string>
545
	<key>PayloadUUID</key>
546
	<string>" . \core\common\Entity::uuid() . "</string>
547
	<key>PayloadVersion</key>
548
	<real>1</real>";
549
        $retval .= $this->proxySettings();
550
        $retval .= "<key>SSID_STR</key>
551
	<string>$ssid</string>
552
</dict>
553
";
554
        \core\common\Entity::outOfThePotatoes();
555
        return $retval;
556
    }
557
558
    const NETWORK_BLOCK_TYPE_SSID = 100;
559
    const NETWORK_BLOCK_TYPE_CONSORTIUMOIS = 101;
560
    const NETWORK_BLOCK_TYPE_WIRED = 102;
561
562
    /**
563
     * produces the entire series of Network blocks; all for SSID-based, 
564
     * Passpoint-based, wired, and manual-select only SSIDs
565
     * 
566
     * @return string
567
     */
568
    private function allNetworkBlocks()
569
    {
570
        $retval = "";
571
        $this->serial = 0;
572
573
        foreach (array_keys($this->attributes['internal:SSID']) as $ssid) {
574
            $retval .= $this->networkBlock(MobileconfigSuperclass::NETWORK_BLOCK_TYPE_SSID, $ssid);
575
        }
576
        if (isset($this->attributes['media:wired']) && get_class($this) == "devices\apple_mobileconfig\Device_mobileconfig_os_x") {
577
            $retval .= $this->networkBlock(MobileconfigSuperclass::NETWORK_BLOCK_TYPE_WIRED, TRUE);
578
        }
579
        if (count($this->attributes['internal:consortia']) > 0 && $this->selectedEapObject->isPasswordRequired() === FALSE) {
580
            foreach ($this->attributes['internal:consortia'] as $oneCons) {
581
                $retval .= $this->networkBlock(MobileconfigSuperclass::NETWORK_BLOCK_TYPE_CONSORTIUMOIS, $oneCons);
582
            }
583
        }
584
        if (isset($this->attributes['media:remove_SSID'])) {
585
            $this->removeSerial = 0;
586
            foreach ($this->attributes['media:remove_SSID'] as $removeSSID) {
587
                $retval .= $this->removenetworkBlock($removeSSID);
588
                $this->removeSerial = $this->removeSerial + 1;
589
            }
590
        }
591
        return $retval;
592
    }
593
594
    /**
595
     * collates a block with all CAs that are to be included in the mobileconfig
596
     * 
597
     * @return string
598
     */
599
    private function allCA()
600
    {
601
        $retval = "";
602
        $this->caSerial = 0;
603
        foreach ($this->attributes['internal:CAs'][0] as $ca) {
604
            $retval .= $this->caBlob($ca);
605
            $this->caSerial = $this->caSerial + 1;
606
        }
607
        return $retval;
608
    }
609
610
    /**
611
     * creates a Cert block containing a client certificate (used in SB only)
612
     * @return array the block itself, and the UUID of the certificate
613
     * @throws Exception
614
     */
615
    private function clientP12Block()
616
    {
617
        \core\common\Entity::intoThePotatoes();
618
        if (!is_array($this->clientCert)) {
619
            throw new Exception("the client block was called but there is no client certificate!");
620
        }
621
        $binaryBlob = $this->clientCert["certdata_nointermediate"];
622
        $mimeBlob = base64_encode($binaryBlob);
623
        $mimeFormatted = chunk_split($mimeBlob, 52, "\r\n");
624
        $payloadUUID = \core\common\Entity::uuid('', $mimeBlob);
625
        $retArray = ["block" => "<dict>" .
626
            // we don't include the import password. It's displayed on screen, and should be input by the user.
627
            // <key>Password</key>
628
            //   <string>" . $this->clientCert['password'] . "</string>
629
            "<key>PayloadCertificateFileName</key>
630
                     <string>" . $this->massagedConsortium . ".pfx</string>
631
                  <key>PayloadContent</key>
632
                     <data>
633
$mimeFormatted
634
                     </data>
635
                  <key>PayloadDescription</key>
636
                     <string>MIME Base-64 encoded PKCS#12 Client Certificate</string>
637
                  <key>PayloadDisplayName</key>
638
                     <string>" . _("eduroam user certificate") . "</string>
639
                  <key>PayloadIdentifier</key>
640
                     <string>com.apple.security.pkcs12.$payloadUUID</string>
641
                  <key>PayloadType</key>
642
                     <string>com.apple.security.pkcs12</string>
643
                  <key>PayloadUUID</key>
644
                     <string>$payloadUUID</string>
645
                  <key>PayloadVersion</key>
646
                     <integer>1</integer>
647
                </dict>",
648
            "UUID" => $payloadUUID,];
649
        \core\common\Entity::outOfThePotatoes();
650
        return $retArray;
651
    }
652
653
    /**
654
     * creates an Expiry block. This is only done in SB; the profile expires
655
     * when the client cert expires.
656
     * 
657
     * @return string
658
     * @throws Exception
659
     */
660
    private function expiryBlock()
661
    {
662
        if (!is_array($this->clientCert)) {
663
            throw new Exception("the expiry block was called but there is no client certificate!");
664
        }
665
        $expiryTime = new \DateTime($this->clientCert['certObject']->expiry);
666
        return "<key>RemovalDate</key>
667
        <date>" . $expiryTime->format("Y-m-d") . "T" . $expiryTime->format("H:i:s") . "Z</date>";
668
    }
669
670
    private $CAsAccountedFor = [];
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
671
672
    /**
673
     * creates a block for one single CA
674
     * 
675
     * @param array $ca the CA for which to generate the XML block
676
     * @return string
677
     */
678
    private function caBlob($ca)
679
    {
680
        \core\common\Entity::intoThePotatoes();
681
        $stream = "";
682
        if (!in_array($ca['uuid'], $this->CAsAccountedFor)) { // skip if this is a duplicate
683
            // cut lines with CERTIFICATE
684
            $stage1 = preg_replace('/-----BEGIN CERTIFICATE-----/', '', $ca['pem']);
685
            $stage2 = preg_replace('/-----END CERTIFICATE-----/', '', $stage1);
686
            $trimmedPem = trim($stage2);
687
688
            $stream = "
689
            <dict>
690
               <key>PayloadCertificateFileName</key>
691
               <string>" . $ca['uuid'] . ".der</string>
692
               <key>PayloadContent</key>
693
               <data>
694
" . $trimmedPem . "</data>
695
               <key>PayloadDescription</key>
696
               <string>" . sprintf(_("The %s Certification Authority"), \core\common\Entity::$nomenclature_inst) . "</string>
697
               <key>PayloadDisplayName</key>
698
               <string>" . 
699
                    /// example: "Identity Provider CA #1 (Root)"
700
                    sprintf(_("%s CA #%d (%s)" ), 
701
                            \core\common\Entity::$nomenclature_inst, 
702
                            count($this->CAsAccountedFor)+1, 
703
                            ($ca['root'] ? _("Root") : _("Intermediate"))) . 
704
              "</string>
705
               <key>PayloadIdentifier</key>
706
               <string>" . self::$iPhonePayloadPrefix . ".$this->massagedConsortium.$this->massagedCountry.$this->massagedInst.$this->massagedProfile.credential.$this->caSerial</string>
707
               <key>PayloadOrganization</key>
708
               <string>" . $this->massagedConsortium . ".1x-config.org</string>
709
               <key>PayloadType</key>
710
               <string>com.apple.security.root</string>
711
               <key>PayloadUUID</key><string>" . $ca['uuid'] . "</string>
712
               <key>PayloadVersion</key>
713
               <integer>1</integer>
714
            </dict>";
715
            $this->CAsAccountedFor[] = $ca['uuid'];
716
        }
717
        \core\common\Entity::outOfThePotatoes();
718
        return $stream;
719
    }
720
721
}
722