| Matthias Andreas Benkard | b382b10 | 2021-01-02 15:32:21 +0100 | [diff] [blame] | 1 | <?php | 
|  | 2 | require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/footer.php'; | 
|  | 3 | logger(); | 
|  | 4 |  | 
|  | 5 | $hash = $js_minifier->getDataHash(); | 
|  | 6 | $JSPath = '/tmp/' . $hash . '.js'; | 
|  | 7 | if(!file_exists($JSPath)) { | 
|  | 8 | $js_minifier->minify($JSPath); | 
|  | 9 | cleanupJS($hash); | 
|  | 10 | } | 
|  | 11 | ?> | 
|  | 12 | <script src="/cache/<?=basename($JSPath)?>"></script> | 
|  | 13 | <script> | 
|  | 14 | <?php | 
|  | 15 | $lang_footer = json_encode($lang['footer']); | 
|  | 16 | $lang_acl = json_encode($lang['acl']); | 
|  | 17 | $lang_tfa = json_encode($lang['tfa']); | 
|  | 18 | $lang_fido2 = json_encode($lang['fido2']); | 
|  | 19 | echo "var lang_footer = ". $lang_footer . ";\n"; | 
|  | 20 | echo "var lang_acl = ". $lang_acl . ";\n"; | 
|  | 21 | echo "var lang_tfa = ". $lang_tfa . ";\n"; | 
|  | 22 | echo "var lang_fido2 = ". $lang_fido2 . ";\n"; | 
|  | 23 | echo "var docker_timeout = ". $DOCKER_TIMEOUT * 1000 . ";\n"; | 
|  | 24 | ?> | 
|  | 25 | $(window).scroll(function() { | 
|  | 26 | sessionStorage.scrollTop = $(this).scrollTop(); | 
|  | 27 | }); | 
|  | 28 | // Select language and reopen active URL without POST | 
|  | 29 | function setLang(sel) { | 
|  | 30 | $.post( "<?= $_SERVER['REQUEST_URI']; ?>", {lang: sel} ); | 
|  | 31 | window.location.href = window.location.pathname + window.location.search; | 
|  | 32 | } | 
|  | 33 | // FIDO2 functions | 
|  | 34 | function arrayBufferToBase64(buffer) { | 
|  | 35 | let binary = ''; | 
|  | 36 | let bytes = new Uint8Array(buffer); | 
|  | 37 | let len = bytes.byteLength; | 
|  | 38 | for (let i = 0; i < len; i++) { | 
|  | 39 | binary += String.fromCharCode( bytes[ i ] ); | 
|  | 40 | } | 
|  | 41 | return window.btoa(binary); | 
|  | 42 | } | 
|  | 43 | function recursiveBase64StrToArrayBuffer(obj) { | 
|  | 44 | let prefix = '=?BINARY?B?'; | 
|  | 45 | let suffix = '?='; | 
|  | 46 | if (typeof obj === 'object') { | 
|  | 47 | for (let key in obj) { | 
|  | 48 | if (typeof obj[key] === 'string') { | 
|  | 49 | let str = obj[key]; | 
|  | 50 | if (str.substring(0, prefix.length) === prefix && str.substring(str.length - suffix.length) === suffix) { | 
|  | 51 | str = str.substring(prefix.length, str.length - suffix.length); | 
|  | 52 | let binary_string = window.atob(str); | 
|  | 53 | let len = binary_string.length; | 
|  | 54 | let bytes = new Uint8Array(len); | 
|  | 55 | for (let i = 0; i < len; i++) { | 
|  | 56 | bytes[i] = binary_string.charCodeAt(i); | 
|  | 57 | } | 
|  | 58 | obj[key] = bytes.buffer; | 
|  | 59 | } | 
|  | 60 | } else { | 
|  | 61 | recursiveBase64StrToArrayBuffer(obj[key]); | 
|  | 62 | } | 
|  | 63 | } | 
|  | 64 | } | 
|  | 65 | } | 
|  | 66 | $(window).load(function() { | 
|  | 67 | $(".overlay").hide(); | 
|  | 68 | }); | 
|  | 69 | $(document).ready(function() { | 
|  | 70 | $(document).on('shown.bs.modal', function(e) { | 
|  | 71 | modal_id = $(e.relatedTarget).data('target'); | 
|  | 72 | $(modal_id).attr("aria-hidden","false"); | 
|  | 73 | }); | 
|  | 74 | // TFA, CSRF, Alerts in footer.inc.php | 
|  | 75 | // Other general functions in mailcow.js | 
|  | 76 | <?php | 
|  | 77 | $alertbox_log_parser = alertbox_log_parser($_SESSION); | 
|  | 78 | if (is_array($alertbox_log_parser)) { | 
|  | 79 | foreach($alertbox_log_parser as $log) { | 
|  | 80 | $alerts[$log['type']][] = $log['msg']; | 
|  | 81 | } | 
|  | 82 | foreach($alerts as $alert_type => $alert_msg) { | 
|  | 83 | ?> | 
|  | 84 | mailcow_alert_box(<?=json_encode(implode('<hr class="alert-hr">', $alert_msg));?>, <?=$alert_type;?>); | 
|  | 85 | <?php | 
|  | 86 | } | 
|  | 87 | unset($_SESSION['return']); | 
|  | 88 | } | 
|  | 89 | ?> | 
|  | 90 | // Confirm TFA modal | 
|  | 91 | <?php if (isset($_SESSION['pending_tfa_method'])):?> | 
|  | 92 | $('#ConfirmTFAModal').modal({ | 
|  | 93 | backdrop: 'static', | 
|  | 94 | keyboard: false | 
|  | 95 | }); | 
|  | 96 | $('#u2f_status_auth').html('<p><span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ' + lang_tfa.init_u2f + '</p>'); | 
|  | 97 | $('#ConfirmTFAModal').on('shown.bs.modal', function(){ | 
|  | 98 | $(this).find('input[name=token]').focus(); | 
|  | 99 | // If U2F | 
|  | 100 | if(document.getElementById("u2f_auth_data") !== null) { | 
|  | 101 | $.ajax({ | 
|  | 102 | type: "GET", | 
|  | 103 | cache: false, | 
|  | 104 | dataType: 'script', | 
|  | 105 | url: "/api/v1/get/u2f-authentication/<?= (isset($_SESSION['pending_mailcow_cc_username'])) ? rawurlencode($_SESSION['pending_mailcow_cc_username']) : null; ?>", | 
|  | 106 | complete: function(data){ | 
|  | 107 | $('#u2f_status_auth').html(lang_tfa.waiting_usb_auth); | 
|  | 108 | data; | 
|  | 109 | setTimeout(function() { | 
|  | 110 | console.log("Ready to authenticate"); | 
|  | 111 | u2f.sign(appId, challenge, registeredKeys, function(data) { | 
|  | 112 | var form = document.getElementById('u2f_auth_form'); | 
|  | 113 | var auth = document.getElementById('u2f_auth_data'); | 
|  | 114 | console.log("Authenticate callback", data); | 
|  | 115 | auth.value = JSON.stringify(data); | 
|  | 116 | form.submit(); | 
|  | 117 | }); | 
|  | 118 | }, 1000); | 
|  | 119 | } | 
|  | 120 | }); | 
|  | 121 | } | 
|  | 122 | }); | 
|  | 123 | $('#ConfirmTFAModal').on('hidden.bs.modal', function(){ | 
|  | 124 | $.ajax({ | 
|  | 125 | type: "GET", | 
|  | 126 | cache: false, | 
|  | 127 | dataType: 'script', | 
|  | 128 | url: '/inc/ajax/destroy_tfa_auth.php', | 
|  | 129 | complete: function(data){ | 
|  | 130 | window.location = window.location.href.split("#")[0]; | 
|  | 131 | } | 
|  | 132 | }); | 
|  | 133 | }); | 
|  | 134 | <?php endif; ?> | 
|  | 135 | // Validate FIDO2 | 
|  | 136 | $("#fido2-login").click(function(){ | 
|  | 137 | $('#fido2-alerts').html(); | 
|  | 138 | if (!window.fetch || !navigator.credentials || !navigator.credentials.create) { | 
|  | 139 | window.alert('Browser not supported.'); | 
|  | 140 | return; | 
|  | 141 | } | 
|  | 142 | window.fetch("/api/v1/get/fido2-get-args", {method:'GET',cache:'no-cache'}).then(function(response) { | 
|  | 143 | return response.json(); | 
|  | 144 | }).then(function(json) { | 
|  | 145 | if (json.success === false) { | 
|  | 146 | throw new Error(); | 
|  | 147 | } | 
|  | 148 | recursiveBase64StrToArrayBuffer(json); | 
|  | 149 | return json; | 
|  | 150 | }).then(function(getCredentialArgs) { | 
|  | 151 | return navigator.credentials.get(getCredentialArgs); | 
|  | 152 | }).then(function(cred) { | 
|  | 153 | return { | 
|  | 154 | id: cred.rawId ? arrayBufferToBase64(cred.rawId) : null, | 
|  | 155 | clientDataJSON: cred.response.clientDataJSON  ? arrayBufferToBase64(cred.response.clientDataJSON) : null, | 
|  | 156 | authenticatorData: cred.response.authenticatorData ? arrayBufferToBase64(cred.response.authenticatorData) : null, | 
|  | 157 | signature : cred.response.signature ? arrayBufferToBase64(cred.response.signature) : null | 
|  | 158 | }; | 
|  | 159 | }).then(JSON.stringify).then(function(AuthenticatorAttestationResponse) { | 
|  | 160 | return window.fetch("/api/v1/process/fido2-args", {method:'POST', body: AuthenticatorAttestationResponse, cache:'no-cache'}); | 
|  | 161 | }).then(function(response) { | 
|  | 162 | return response.json(); | 
|  | 163 | }).then(function(json) { | 
|  | 164 | if (json.success) { | 
|  | 165 | window.location = window.location.href.split("#")[0]; | 
|  | 166 | } else { | 
|  | 167 | throw new Error(); | 
|  | 168 | } | 
|  | 169 | }).catch(function(err) { | 
|  | 170 | if (typeof err.message === 'undefined') { | 
|  | 171 | mailcow_alert_box(lang_fido2.fido2_validation_failed, "danger"); | 
|  | 172 | } else { | 
|  | 173 | mailcow_alert_box(lang_fido2.fido2_validation_failed + ":<br><i>" + err.message + "</i>", "danger"); | 
|  | 174 | } | 
|  | 175 | }); | 
|  | 176 | }); | 
|  | 177 | // Set TFA/FIDO2 | 
|  | 178 | $("#register-fido2").click(function(){ | 
|  | 179 | $("option:selected").prop("selected", false); | 
|  | 180 | if (!window.fetch || !navigator.credentials || !navigator.credentials.create) { | 
|  | 181 | window.alert('Browser not supported.'); | 
|  | 182 | return; | 
|  | 183 | } | 
|  | 184 | window.fetch("/api/v1/get/fido2-registration/<?= (isset($_SESSION['mailcow_cc_username'])) ? rawurlencode($_SESSION['mailcow_cc_username']) : null; ?>", {method:'GET',cache:'no-cache'}).then(function(response) { | 
|  | 185 | return response.json(); | 
|  | 186 | }).then(function(json) { | 
|  | 187 | if (json.success === false) { | 
|  | 188 | throw new Error(json.msg); | 
|  | 189 | } | 
|  | 190 | recursiveBase64StrToArrayBuffer(json); | 
|  | 191 | return json; | 
|  | 192 | }).then(function(createCredentialArgs) { | 
|  | 193 | console.log(createCredentialArgs); | 
|  | 194 | return navigator.credentials.create(createCredentialArgs); | 
|  | 195 | }).then(function(cred) { | 
|  | 196 | return { | 
|  | 197 | clientDataJSON: cred.response.clientDataJSON  ? arrayBufferToBase64(cred.response.clientDataJSON) : null, | 
|  | 198 | attestationObject: cred.response.attestationObject ? arrayBufferToBase64(cred.response.attestationObject) : null | 
|  | 199 | }; | 
|  | 200 | }).then(JSON.stringify).then(function(AuthenticatorAttestationResponse) { | 
|  | 201 | return window.fetch("/api/v1/add/fido2-registration", {method:'POST', body: AuthenticatorAttestationResponse, cache:'no-cache'}); | 
|  | 202 | }).then(function(response) { | 
|  | 203 | return response.json(); | 
|  | 204 | }).then(function(json) { | 
|  | 205 | if (json.success) { | 
|  | 206 | window.location = window.location.href.split("#")[0]; | 
|  | 207 | } else { | 
|  | 208 | throw new Error(json.msg); | 
|  | 209 | } | 
|  | 210 | }).catch(function(err) { | 
|  | 211 | $('#fido2-alerts').html('<span class="text-danger"><b>' + err.message + '</b></span>'); | 
|  | 212 | }); | 
|  | 213 | }); | 
|  | 214 | $('#selectTFA').change(function () { | 
|  | 215 | if ($(this).val() == "yubi_otp") { | 
|  | 216 | $('#YubiOTPModal').modal('show'); | 
|  | 217 | $("option:selected").prop("selected", false); | 
|  | 218 | } | 
|  | 219 | if ($(this).val() == "totp") { | 
|  | 220 | $('#TOTPModal').modal('show'); | 
|  | 221 | request_token = $('#tfa-qr-img').data('totp-secret'); | 
|  | 222 | $.ajax({ | 
|  | 223 | url: '/inc/ajax/qr_gen.php', | 
|  | 224 | data: { | 
|  | 225 | token: request_token, | 
|  | 226 | }, | 
|  | 227 | }).done(function (result) { | 
|  | 228 | $("#tfa-qr-img").attr("src", result); | 
|  | 229 | }); | 
|  | 230 | $("option:selected").prop("selected", false); | 
|  | 231 | } | 
|  | 232 | if ($(this).val() == "u2f") { | 
|  | 233 | $('#U2FModal').modal('show'); | 
|  | 234 | $("option:selected").prop("selected", false); | 
|  | 235 | $("#start_u2f_register").click(function(){ | 
|  | 236 | $('#u2f_return_code').html(''); | 
|  | 237 | $('#u2f_return_code').hide(); | 
|  | 238 | $('#u2f_status_reg').html('<p><span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ' + lang_tfa.init_u2f + '</p>'); | 
|  | 239 | $.ajax({ | 
|  | 240 | type: "GET", | 
|  | 241 | cache: false, | 
|  | 242 | dataType: 'script', | 
|  | 243 | url: "/api/v1/get/u2f-registration/<?= (isset($_SESSION['mailcow_cc_username'])) ? rawurlencode($_SESSION['mailcow_cc_username']) : null; ?>", | 
|  | 244 | complete: function(data){ | 
|  | 245 | data; | 
|  | 246 | setTimeout(function() { | 
|  | 247 | console.log("Ready to register"); | 
|  | 248 | $('#u2f_status_reg').html(lang_tfa.waiting_usb_register); | 
|  | 249 | u2f.register(appId, registerRequests, registeredKeys, function(deviceResponse) { | 
|  | 250 | var form  = document.getElementById('u2f_reg_form'); | 
|  | 251 | var reg   = document.getElementById('u2f_register_data'); | 
|  | 252 | console.log("Register callback: ", data); | 
|  | 253 | if (deviceResponse.errorCode && deviceResponse.errorCode != 0) { | 
|  | 254 | var u2f_return_code = document.getElementById('u2f_return_code'); | 
|  | 255 | u2f_return_code.style.display = u2f_return_code.style.display === 'none' ? '' : null; | 
|  | 256 | if (deviceResponse.errorCode == "4") { | 
|  | 257 | deviceResponse.errorCode = "4 - The presented device is not eligible for this request. For a registration request this may mean that the token is already registered, and for a sign request it may mean that the token does not know the presented key handle"; | 
|  | 258 | } | 
|  | 259 | else if (deviceResponse.errorCode == "5") { | 
|  | 260 | deviceResponse.errorCode = "5 - Timeout reached before request could be satisfied."; | 
|  | 261 | } | 
|  | 262 | u2f_return_code.innerHTML = lang_tfa.error_code + ': ' + deviceResponse.errorCode + ' ' + lang_tfa.reload_retry; | 
|  | 263 | return; | 
|  | 264 | } | 
|  | 265 | reg.value = JSON.stringify(deviceResponse); | 
|  | 266 | form.submit(); | 
|  | 267 | }); | 
|  | 268 | }, 1000); | 
|  | 269 | } | 
|  | 270 | }); | 
|  | 271 | }); | 
|  | 272 | } | 
|  | 273 | if ($(this).val() == "none") { | 
|  | 274 | $('#DisableTFAModal').modal('show'); | 
|  | 275 | $("option:selected").prop("selected", false); | 
|  | 276 | } | 
|  | 277 | }); | 
|  | 278 |  | 
|  | 279 | // Reload after session timeout | 
|  | 280 | var session_lifetime = <?=((int)$SESSION_LIFETIME * 1000) + 15000;?>; | 
|  | 281 | <?php | 
|  | 282 | if (isset($_SESSION['mailcow_cc_username'])): | 
|  | 283 | ?> | 
|  | 284 | setTimeout(function() { | 
|  | 285 | location.reload(); | 
|  | 286 | }, session_lifetime); | 
|  | 287 | <?php | 
|  | 288 | endif; | 
|  | 289 | ?> | 
|  | 290 |  | 
|  | 291 | // CSRF | 
|  | 292 | $('<input type="hidden" value="<?= $_SESSION['CSRF']['TOKEN']; ?>">').attr('name', 'csrf_token').appendTo('form'); | 
|  | 293 | if (sessionStorage.scrollTop != "undefined") { | 
|  | 294 | $(window).scrollTop(sessionStorage.scrollTop); | 
|  | 295 | } | 
|  | 296 | }); | 
|  | 297 | </script> | 
|  | 298 |  | 
|  | 299 | <div class="container footer"> | 
|  | 300 | <?php if (!empty($UI_TEXTS['ui_footer'])) { ?> | 
|  | 301 | <hr><span class="rot-enc"><?=str_rot13($UI_TEXTS['ui_footer']);?></span> | 
|  | 302 | <?php } ?> | 
|  | 303 | </div> | 
|  | 304 | </body> | 
|  | 305 | </html> | 
|  | 306 | <?php | 
|  | 307 | $stmt = null; | 
|  | 308 | $pdo = null; |