├── help.ico ├── icon.ico ├── info.ico ├── exclam.ico ├── CredentialProvider ├── githash.h ├── resource.h ├── Resource.rc ├── Resource1.rc ├── Resource2.rc ├── tileimage.bmp ├── CredentialProvider.rc ├── CredentialProvider.def ├── resource2.h ├── resource3.h ├── resource1.h ├── Mode.h ├── guid.cpp ├── Dll.h ├── guid.h ├── CredentialProvider.vcxproj.filters ├── Translator.h ├── RCa03772 ├── RCb03772 ├── RCa09068 ├── RCa11524 ├── core │ ├── CProvider.h │ └── CCredential.h ├── helpers.h ├── Dll.cpp ├── Translator.cpp ├── RCa01848 ├── RCb01848 ├── RCc01848 ├── Utilities.h └── Configuration.h ├── WixUIBannerBmp.bmp ├── WixUIDialogBmp.bmp ├── doc ├── install_images │ ├── orca.png │ ├── install1.png │ └── install2.png ├── index.rst ├── introduction.rst ├── maintenance_support.rst ├── installation.rst └── Makefile ├── RegistryHelpers ├── RegisterFilter.reg ├── RegisterProvider.reg ├── UnregisterFilter.reg ├── UnregisterProvider.reg ├── UnegisterForSafeMode.reg └── RegisterForSafeMode.reg ├── CredentialProviderFilter ├── tileimage.bmp ├── CredentialProviderFilter.def ├── resource.h ├── guid.cpp ├── guid.h ├── CredentialProviderFilter.vcxproj.filters ├── CCredentialProviderFilter.h ├── resources.rc └── CCredentialProviderFilter.cpp ├── .gitignore ├── CppClient ├── CppClient │ ├── StoredWebAuthnCredential.h │ ├── FIDOException.h │ ├── FIDORegistrationRequest.h │ ├── AuthenticationStatus.h │ ├── FIDOSignResponse.h │ ├── Challenge.h │ ├── FIDOSignRequest.h │ ├── FIDORegistrationResponse.h │ ├── RegistryReader.h │ ├── OfflineData.h │ ├── Endpoint.h │ ├── PIConfig.h │ ├── CppClient.vcxproj.filters │ ├── Logger.h │ ├── Cpp Client.vcxproj.filters │ ├── PIResponse.h │ ├── Convert.h │ ├── FIDODevice.h │ ├── JsonParser.h │ ├── OfflineHandler.h │ ├── Logger.cpp │ ├── PIResponse.cpp │ ├── RegistryReader.cpp │ ├── PrivacyIDEA.h │ └── CppClient.vcxproj └── CppClient.sln ├── .readthedocs.yaml ├── Shared ├── Shared.vcxproj.filters ├── Shared.h ├── Shared.cpp └── Shared.vcxproj ├── locales ├── en.json ├── de.json └── es.json ├── versioning └── version.h ├── WiXSetup ├── ConfigurationDlgsLocales.wxl ├── Config.wxi └── WiXSetup.wixproj ├── README.md └── PrivacyIDEA-CredentialProvider.sln /help.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/help.ico -------------------------------------------------------------------------------- /icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/icon.ico -------------------------------------------------------------------------------- /info.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/info.ico -------------------------------------------------------------------------------- /exclam.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/exclam.ico -------------------------------------------------------------------------------- /CredentialProvider/githash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define GIT_HASH "fffffffffffffffffffffffffffffffff" 3 | -------------------------------------------------------------------------------- /WixUIBannerBmp.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/WixUIBannerBmp.bmp -------------------------------------------------------------------------------- /WixUIDialogBmp.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/WixUIDialogBmp.bmp -------------------------------------------------------------------------------- /doc/install_images/orca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/doc/install_images/orca.png -------------------------------------------------------------------------------- /CredentialProvider/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/CredentialProvider/resource.h -------------------------------------------------------------------------------- /CredentialProvider/Resource.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/CredentialProvider/Resource.rc -------------------------------------------------------------------------------- /CredentialProvider/Resource1.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/CredentialProvider/Resource1.rc -------------------------------------------------------------------------------- /CredentialProvider/Resource2.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/CredentialProvider/Resource2.rc -------------------------------------------------------------------------------- /CredentialProvider/tileimage.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/CredentialProvider/tileimage.bmp -------------------------------------------------------------------------------- /doc/install_images/install1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/doc/install_images/install1.png -------------------------------------------------------------------------------- /doc/install_images/install2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/doc/install_images/install2.png -------------------------------------------------------------------------------- /RegistryHelpers/RegisterFilter.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/RegistryHelpers/RegisterFilter.reg -------------------------------------------------------------------------------- /RegistryHelpers/RegisterProvider.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/RegistryHelpers/RegisterProvider.reg -------------------------------------------------------------------------------- /RegistryHelpers/UnregisterFilter.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/RegistryHelpers/UnregisterFilter.reg -------------------------------------------------------------------------------- /CredentialProviderFilter/tileimage.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/CredentialProviderFilter/tileimage.bmp -------------------------------------------------------------------------------- /RegistryHelpers/UnregisterProvider.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/RegistryHelpers/UnregisterProvider.reg -------------------------------------------------------------------------------- /CredentialProvider/CredentialProvider.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/privacyidea/privacyidea-credential-provider/HEAD/CredentialProvider/CredentialProvider.rc -------------------------------------------------------------------------------- /CredentialProvider/CredentialProvider.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | DllCanUnloadNow PRIVATE 3 | DllGetClassObject PRIVATE 4 | -------------------------------------------------------------------------------- /CredentialProviderFilter/CredentialProviderFilter.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | DllCanUnloadNow PRIVATE 3 | DllGetClassObject PRIVATE 4 | -------------------------------------------------------------------------------- /RegistryHelpers/UnegisterForSafeMode.reg: -------------------------------------------------------------------------------- 1 | Windows Registry Editor Version 5.00 2 | 3 | [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers] 4 | "ProhibitFallbacks"=- -------------------------------------------------------------------------------- /RegistryHelpers/RegisterForSafeMode.reg: -------------------------------------------------------------------------------- 1 | Windows Registry Editor Version 5.00 2 | 3 | [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers] 4 | "ProhibitFallbacks"=dword:1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | *.obj 3 | *.tlog 4 | *.pdb 5 | *.user 6 | *.wixpdb 7 | *.msm 8 | *.aps 9 | Credential_Provider_Technical_Reference.xps 10 | Tests/Tests.vcxproj.user 11 | x64/ 12 | [Dd]ebug/ 13 | [Rr]elease/ 14 | [Bb]in/ 15 | [Oo]bj/ 16 | nlohmann/ 17 | lib/ 18 | CppClient/nlohmann/json.hpp 19 | /libfido2-1.15.0-nfc-enabled 20 | -------------------------------------------------------------------------------- /CredentialProvider/resource2.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Resource1.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /CredentialProvider/resource3.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Resource2.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /CredentialProvider/resource1.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Resource.rc 4 | // 5 | 6 | // Next default values for new objects 7 | // 8 | #ifdef APSTUDIO_INVOKED 9 | #ifndef APSTUDIO_READONLY_SYMBOLS 10 | #define _APS_NEXT_RESOURCE_VALUE 105 11 | #define _APS_NEXT_COMMAND_VALUE 40001 12 | #define _APS_NEXT_CONTROL_VALUE 1006 13 | #define _APS_NEXT_SYMED_VALUE 101 14 | #endif 15 | #endif 16 | -------------------------------------------------------------------------------- /CredentialProviderFilter/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by resources.rc 4 | // 5 | 6 | // Next default values for new objects 7 | // 8 | #ifdef APSTUDIO_INVOKED 9 | #ifndef APSTUDIO_READONLY_SYMBOLS 10 | #define _APS_NEXT_RESOURCE_VALUE 101 11 | #define _APS_NEXT_COMMAND_VALUE 40001 12 | #define _APS_NEXT_CONTROL_VALUE 1000 13 | #define _APS_NEXT_SYMED_VALUE 101 14 | #endif 15 | #endif 16 | -------------------------------------------------------------------------------- /CppClient/CppClient/StoredWebAuthnCredential.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace privacyidea 5 | { 6 | struct StoredWebAuthnCredential 7 | { 8 | std::string user; 9 | std::string domain; 10 | std::string serial; 11 | std::string cosePublicKey; 12 | std::string credentialId; 13 | std::string rpId; 14 | std::vector transports; 15 | std::string type = "public-key"; 16 | int timeout = 60000; 17 | std::string userVerification; 18 | int signCount = 0; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. privacyIDEA Credential Provider documentation master file, created by 2 | sphinx-quickstart on Tue Jun 7 16:25:49 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | privacyIDEA Credential Provider 7 | =============================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | introduction 15 | installation 16 | configuration 17 | maintenance_support 18 | 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | 28 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: doc/conf.py 17 | 18 | # We recommend specifying your dependencies to enable reproducible builds: 19 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | # python: 21 | # install: 22 | # - requirements: docs/requirements.txt 23 | -------------------------------------------------------------------------------- /CredentialProvider/Mode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class Mode 4 | { 5 | NO_CHANGE = 0, 6 | CHANGE_PASSWORD = 6, 7 | 8 | USERNAME = 10, 9 | PASSWORD = 11, 10 | USERNAMEPASSWORD = 12, // Required for send_pass. 11 | 12 | PRIVACYIDEA = 13, 13 | SEC_KEY_ANY = 15, 14 | PASSKEY = 16, 15 | 16 | SEC_KEY_REG = 17, 17 | SEC_KEY_REG_PIN = 18, 18 | 19 | SEC_KEY_PIN = 21, 20 | SEC_KEY_NO_PIN = 22, // Requires reset with autoLogon to get to CCredential::Connect directly 21 | SEC_KEY_NO_DEVICE = 23, // Requires reset with autoLogon to get to CCredential::Connect directly 22 | }; 23 | 24 | template 25 | constexpr bool IsModeOneOf(Mode mode, Modes... modes) noexcept 26 | { 27 | return ((mode == modes) || ...); 28 | } 29 | -------------------------------------------------------------------------------- /CredentialProvider/guid.cpp: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | ** 17 | ** * * * * * * * * * * * * * * * * * * */ 18 | 19 | #include 20 | #include "guid.h" 21 | -------------------------------------------------------------------------------- /CredentialProviderFilter/guid.cpp: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | ** 17 | ** * * * * * * * * * * * * * * * * * * */ 18 | 19 | #include 20 | #include "guid.h" 21 | -------------------------------------------------------------------------------- /CppClient/CppClient/FIDOException.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "fido/err.h" 5 | 6 | class FIDOException : public std::runtime_error 7 | { 8 | public: 9 | explicit FIDOException(int fido_err_code, const std::string& message = "") 10 | : std::runtime_error( 11 | message.empty() ? 12 | "FIDO2 Error: " + std::string(fido_strerr(fido_err_code)) + " (Code: " + std::to_string(fido_err_code) + ")" : 13 | message + " (FIDO2 Code: " + std::to_string(fido_err_code) + ", " + std::string(fido_strerr(fido_err_code)) + ")" 14 | ), 15 | _fido_error_code(fido_err_code) 16 | {} 17 | 18 | explicit FIDOException(const std::string& message) 19 | : std::runtime_error(message), _fido_error_code(FIDO_ERR_INTERNAL) 20 | {} 21 | 22 | 23 | int getErrorCode() const noexcept 24 | { 25 | return _fido_error_code; 26 | } 27 | 28 | private: 29 | int _fido_error_code; 30 | }; -------------------------------------------------------------------------------- /CppClient/CppClient/FIDORegistrationRequest.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class FIDORegistrationRequest 7 | { 8 | public: 9 | std::string rpId = ""; 10 | std::string rpName = ""; 11 | std::string userName = ""; 12 | std::string userDisplayName = ""; 13 | std::string userId = ""; 14 | std::string challenge = ""; 15 | std::string serial = ""; 16 | std::string transactionId = ""; 17 | std::string type = ""; 18 | // List of {"type", identifier}, e.g. {"public-key", "-7"} for ES256 19 | std::vector> pubKeyCredParams; 20 | std::vector excludeCredentials; 21 | std::string attestation = "none"; 22 | // residentKey, requireResidentKey, userVerification, ... 23 | std::vector> authenticatorSelection; 24 | bool residentKey = true; 25 | bool userVerification = true; 26 | 27 | int timeout = 120000; 28 | }; -------------------------------------------------------------------------------- /doc/introduction.rst: -------------------------------------------------------------------------------- 1 | .. _introduction: 2 | 3 | Introduction 4 | ============ 5 | 6 | The privacyIDEA Credential Provider is a tool to improve the logon security 7 | of your Windows Desktops, Servers and Windows Terminal Servers. 8 | It is used to add a second factor for authentication, when logging into your 9 | Windows system. 10 | 11 | The privacyIDEA Credential Provider does this by communicating with the privacyIDEA Authentication System [#privacyidea]_. 12 | The privacyIDEA Authentication System can manage many different kinds of second factors for the domain users: Passkeys/WebAuthn token, 13 | PUSH token, Classic OTP token like HOTP, TOTP, Email or SMS, Yubikeys and many more! 14 | 15 | Additonally, privacyIDEA can provide flexible MFA workflows with its powerful policy and event system. The privacyIDEA Credential Provider 16 | also offers a wide range of configuration options to customzie the login to your needs. 17 | 18 | .. [#privacyidea] https://privacyidea.org 19 | -------------------------------------------------------------------------------- /CppClient/CppClient/AuthenticationStatus.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #pragma once 21 | enum class AuthenticationStatus 22 | { 23 | NOT_SET = 0, 24 | ACCEPT = 1, 25 | CHALLENGE = 2, 26 | REJECT = 3, 27 | }; -------------------------------------------------------------------------------- /doc/maintenance_support.rst: -------------------------------------------------------------------------------- 1 | .. _maintenance_support: 2 | 3 | Development, Maintenance and Support 4 | ==================================== 5 | 6 | The privacyIDEA Credential Provider was first developed by Last Squirrel IT [#lastsquirrel]_. 7 | The company has a long experience in Microsoft Windows security tools. They developed many different credential providers and 8 | plugins for Active Directory Federation Services. 9 | 10 | Since 2018 the development is continued by NetKnights. 11 | You will get maintenance and support via the company NetKnights [#netknights]_. 12 | NetKnights also maintains the privacyIDEA Authentication System and issues 13 | different service level agreements [#sla]_ for the privacyIDEA Credential 14 | Provider, the privacyIDEA Authentication System and many other plugins. 15 | 16 | 17 | .. [#lastsquirrel] http://www.lastsquirrel.com 18 | .. [#netknights] https://netknights.it 19 | .. [#sla] https://netknights.it/en/produkte/privacyidea-credential-provider/ 20 | 21 | -------------------------------------------------------------------------------- /CredentialProvider/Dll.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | ** 17 | ** * * * * * * * * * * * * * * * * * * */ 18 | 19 | #pragma once 20 | 21 | #include "helpers.h" 22 | #include 23 | #include 24 | 25 | // global dll hinstance 26 | extern HINSTANCE g_hinst; 27 | #define HINST_THISDLL g_hinst 28 | 29 | void DllAddRef() noexcept; 30 | void DllRelease() noexcept; 31 | -------------------------------------------------------------------------------- /CredentialProvider/guid.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | ** 17 | ** * * * * * * * * * * * * * * * * * * */ 18 | 19 | #ifndef _GUID_H 20 | #define _GUID_H 21 | #pragma once 22 | 23 | #include 24 | 25 | // {7BAF541E-F8E0-4EDF-B69A-BD2771139E8E} 26 | DEFINE_GUID(CLSID_CSample, 27 | 0x7baf541e, 0xf8e0, 0x4edf, 0xb6, 0x9a, 0xbd, 0x27, 0x71, 0x13, 0x9e, 0x8e); 28 | 29 | #endif -------------------------------------------------------------------------------- /CppClient/CppClient/FIDOSignResponse.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | #pragma once 20 | #include 21 | 22 | struct FIDOSignResponse 23 | { 24 | std::string credentialid; 25 | std::string clientdata; 26 | std::string authenticatordata; 27 | std::string signaturedata; 28 | std::string userHandle; 29 | }; 30 | -------------------------------------------------------------------------------- /Shared/Shared.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | -------------------------------------------------------------------------------- /CppClient/CppClient/Challenge.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | #pragma once 20 | #include "FIDOSignRequest.h" 21 | #include 22 | #include 23 | 24 | class Challenge 25 | { 26 | public: 27 | std::string message; 28 | std::string transactionId; 29 | std::string serial; 30 | std::string type; 31 | std::string image; 32 | std::string clientMode; 33 | std::optional fidoSignRequest = std::nullopt; 34 | }; 35 | -------------------------------------------------------------------------------- /CredentialProviderFilter/guid.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | ** 17 | ** * * * * * * * * * * * * * * * * * * */ 18 | 19 | // {34065473-D75F-4BC2-9782-E98E63ED0D41} 20 | DEFINE_GUID(CLSID_CSample, 21 | 0x34065473, 0xd75f, 0x4bc2, 0x97, 0x82, 0xe9, 0x8e, 0x63, 0xed, 0xd, 0x41); 22 | 23 | 24 | // GUID of primary Credential Provider 25 | // {7BAF541E-F8E0-4EDF-B69A-BD2771139E8E} 26 | DEFINE_GUID(CLSID_COTP_LOGON, 27 | 0x7baf541e, 0xf8e0, 0x4edf, 0xb6, 0x9a, 0xbd, 0x27, 0x71, 0x13, 0x9e, 0x8e); 28 | 29 | 30 | // ADD ADDITIONAL HERE -------------------------------------------------------------------------------- /Shared/Shared.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2020 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #pragma once 21 | #include 22 | #include 23 | /* Methods that are used by both the CredentialProvider and the Filter */ 24 | namespace Shared 25 | { 26 | #define PROVIDER 0 27 | #define FILTER 1 28 | bool IsRequiredForScenario(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, int caller); 29 | 30 | bool IsCurrentSessionRemote(); 31 | 32 | std::string CPUStoString(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus); 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /CredentialProvider/CredentialProvider.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /CppClient/CppClient.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34031.279 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppClient", "CppClient\CppClient.vcxproj", "{6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Debug|x64.ActiveCfg = Debug|x64 17 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Debug|x64.Build.0 = Debug|x64 18 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Debug|x86.ActiveCfg = Debug|Win32 19 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Debug|x86.Build.0 = Debug|Win32 20 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Release|x64.ActiveCfg = Release|x64 21 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Release|x64.Build.0 = Release|x64 22 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Release|x86.ActiveCfg = Release|Win32 23 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4CC99074-7674-465D-A290-F234FF84590E} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "Username", 3 | "1": "Password", 4 | "2": "Old Password", 5 | "3": "New Password", 6 | "4": "Confirm password", 7 | "5": "Sign in to:", 8 | "6": "One-Time Password", 9 | "7": "Wrong One-Time Password!", 10 | "8": "Reset Login", 11 | "9": "Available offline token:\n", 12 | "10": "OTPs left", 13 | "11": "Connection or configuration error! Check the logfile.", 14 | "12": "Use Security Key", 15 | "13": "Use One-Time-Password", 16 | "14": "PIN", 17 | "15": "Touch your Security Key!", 18 | "16": "Connecting to privacyIDEA...", 19 | "17": "privacyIDEA Login", 20 | "18": "Enter your One-Time-Password", 21 | "19": "No matching credentials on this security key found!", 22 | "20": "Connect your security key!", 23 | "21": "Authentication successful!\nChecking offline status for this token...", 24 | "22": "Refilling offline token...", 25 | "23": "Remove and insert your device again!", 26 | "24": "Communicating with the device failed. Try another authentication method or device.", 27 | "25": "Wrong PIN, please try again!", 28 | "26": "Use Passkey", 29 | "27": "Enter your username", 30 | "28": "Enter your password", 31 | "29": "Enter your username and password", 32 | "30": "Passkey Registration. Touch your security key!", 33 | "31": "Enter the PIN of your security key!", 34 | "32": "Passkey Registration", 35 | "33": "Log in with Username", 36 | "34": "FIDO Authentication cancelled. Try another way.", 37 | "35": "Not now", 38 | "36": "Offline FIDO", 39 | "37": "No matching credentials on the security key." 40 | } 41 | -------------------------------------------------------------------------------- /CppClient/CppClient/FIDOSignRequest.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | struct AllowCredential 6 | { 7 | std::string id; 8 | std::vector transports; 9 | std::string type = "public-key"; 10 | }; 11 | 12 | 13 | struct FIDOSignRequest 14 | { 15 | std::vector allowCredentials; 16 | std::string challenge; 17 | std::string rpId; 18 | std::string userVerification; 19 | std::vector transports; 20 | std::string type; // Token type, "webauthn" or "passkey" 21 | std::string transactionId; 22 | std::string message; 23 | int timeout = 0; 24 | 25 | FIDOSignRequest() = default; 26 | 27 | FIDOSignRequest( 28 | const std::string& challenge, 29 | const std::string& rpId, 30 | const std::string& userVerification, 31 | const std::string& transactionId, 32 | const std::string& message, 33 | const std::string& type, 34 | const std::vector& allowCredentials = std::vector(), 35 | const std::vector& transports = std::vector(), 36 | int timeout = 0) 37 | : challenge(challenge), rpId(rpId), userVerification(userVerification), transactionId(transactionId), message(message), 38 | type(type), allowCredentials(allowCredentials), transports(transports), timeout(timeout) 39 | {} 40 | 41 | std::string ToString() 42 | { 43 | // convert all non vectors to string and concatenate 44 | return "FIDO2SignRequest: challenge: " + challenge + 45 | ", rpId: " + rpId + 46 | ", userVerification: " + userVerification + 47 | ", transactionId: " + transactionId + 48 | ", message: " + message + 49 | ", type: " + type ; 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /CppClient/CppClient/FIDORegistrationResponse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class FIDORegistrationResponse 5 | { 6 | public: 7 | FIDORegistrationResponse() = default; 8 | 9 | FIDORegistrationResponse( 10 | std::string authenticatorData, 11 | std::string signature, 12 | std::string credentialId, 13 | std::string largeBlobKey, 14 | std::string clientDataHash, 15 | std::string aaguid, 16 | std::string publicKey, 17 | std::string x5c, 18 | std::string attestationObject, 19 | std::string clientDataJSON, 20 | std::string authenticatorAttachment, 21 | std::string userHandle) 22 | : authenticatorData(std::move(authenticatorData)), 23 | signature(std::move(signature)), 24 | credentialId(std::move(credentialId)), 25 | largeBlobKey(std::move(largeBlobKey)), 26 | clientDataHash(std::move(clientDataHash)), 27 | aaguid(std::move(aaguid)), 28 | publicKey(std::move(publicKey)), 29 | x5c(std::move(x5c)), 30 | attestationObject(std::move(attestationObject)), 31 | clientDataJSON(std::move(clientDataJSON)), 32 | authenticatorAttachment(std::move(authenticatorAttachment)), 33 | userHandle(std::move(userHandle)) 34 | {} 35 | 36 | std::string authenticatorData; 37 | std::string signature; 38 | std::string credentialId; 39 | std::string largeBlobKey; 40 | std::string clientDataHash; 41 | std::string aaguid; 42 | std::string publicKey; 43 | std::string x5c; 44 | std::string attestationObject; 45 | std::string clientDataJSON; 46 | std::string authenticatorAttachment; 47 | std::string userHandle; 48 | }; -------------------------------------------------------------------------------- /CppClient/CppClient/RegistryReader.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | #pragma once 20 | #include 21 | #include 22 | #include 23 | 24 | constexpr auto CONFIG_REGISTRY_PATH = L"SOFTWARE\\Netknights GmbH\\PrivacyIDEA-CP\\"; 25 | constexpr auto REALM_MAPPING_REGISTRY_PATH = L"SOFTWARE\\Netknights GmbH\\PrivacyIDEA-CP\\realm-mapping"; 26 | constexpr auto LAST_USER_REGISTRY_PATH = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Authentication\\LogonUI"; 27 | 28 | class RegistryReader 29 | { 30 | public: 31 | 32 | RegistryReader(const std::wstring& pathToKey) noexcept; 33 | 34 | std::wstring path; 35 | 36 | // puts all keys and values from the current path into a map, the keys will be converted to uppercase 37 | bool GetAll(const std::wstring& pathToKey, std::map& map) noexcept; 38 | 39 | std::wstring GetWString(std::wstring name) noexcept; 40 | 41 | bool GetBool(std::wstring name) noexcept; 42 | 43 | int GetInt(std::wstring name) noexcept; 44 | 45 | std::vector GetMultiSZ(const std::wstring& valueName) noexcept; 46 | }; 47 | -------------------------------------------------------------------------------- /versioning/version.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 2025 NetKnights GmbH 5 | ** 6 | ** Author Dominik Pretzsch 7 | ** Nils Behlen 8 | ** 9 | ** Licensed under the Apache License, Version 2.0 (the "License"); 10 | ** you may not use this file except in compliance with the License. 11 | ** You may obtain a copy of the License at 12 | ** 13 | ** http://www.apache.org/licenses/LICENSE-2.0 14 | ** 15 | ** Unless required by applicable law or agreed to in writing, software 16 | ** distributed under the License is distributed on an "AS IS" BASIS, 17 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | ** See the License for the specific language governing permissions and 19 | ** limitations under the License. 20 | ** 21 | ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 22 | 23 | #ifndef _VERSION_H 24 | #define _VERSION_H 25 | #pragma once 26 | 27 | #define STRINGIZE2(s) #s 28 | #define STRINGIZE(s) STRINGIZE2(s) 29 | 30 | #define VERSION_MAJOR 3 31 | #define VERSION_MINOR 7 32 | #define VERSION_BUILD 0 33 | #define VERSION_REVISION 3 34 | 35 | #define VER_FILE_DESCRIPTION_STR "privacyIDEA Credential Provider for Windows logon" 36 | #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION 37 | #define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \ 38 | "." STRINGIZE(VERSION_MINOR) \ 39 | "." STRINGIZE(VERSION_BUILD) \ 40 | "." STRINGIZE(VERSION_REVISION) \ 41 | 42 | #define VER_PRODUCTNAME_STR "privacyIDEA CredentialProvider" 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "Benutzername", 3 | "1": "Kennwort", 4 | "2": "Altes Kennwort", 5 | "3": "Neues Kennwort", 6 | "4": "Kennwort bestätigen", 7 | "5": "Anmelden an:", 8 | "6": "Einmalpasswort", 9 | "7": "Falsches Einmalpasswort!", 10 | "8": "Login zurücksetzen", 11 | "9": "Verfügbare Offline Token:\n", 12 | "10": "OTPs verbleibend", 13 | "11": "Verbindungs- oder Konfigurationsfehler! Prüfen Sie die Log Datei.", 14 | "12": "Sicherheitsschlüssel verwenden", 15 | "13": "Einmalpasswort verwenden", 16 | "14": "PIN", 17 | "15": "Berühren Sie Ihren Sicherheitsschlüssel!", 18 | "16": "Verbinde mit privacyIDEA...", 19 | "17": "privacyIDEA Login", 20 | "18": "Geben Sie Ihr Einmalpasswort ein", 21 | "19": "Auf diesem Sicherheitsschlüssel sind keine passenden Anmeldedaten!", 22 | "20": "Verbinden Sie Ihren Sicherheitsschlüssel!", 23 | "21": "Authentisierung erfolgreich! Offline Status für diesen Token wird geprüft...", 24 | "22": "Offline Token werden aufgefüllt...", 25 | "23": "Entfernen Sie Ihren Sicherheitsschlüssel und verbinden Sie Ihn erneut!", 26 | "24": "Kommunikation mit dem Gerät fehlgeschlagen. Versuchen Sie eine andere Anmeldeoption oder ein anderes Gerät.", 27 | "25": "Falsche PIN. Bitte erneut eingeben", 28 | "26": "Passkey verwenden", 29 | "27": "Geben Sie Ihren Benutzernamen ein", 30 | "28": "Geben Sie Ihr Kennwort ein", 31 | "29": "Geben Sie Benutzernamen und Kennwort ein", 32 | "30": "Passkey Registrierung. Berühren Sie Ihren Sicherheitsschlüssel!", 33 | "31": "Geben Sie die PIN Ihres Sicherheitsschlüssels ein!", 34 | "32": "Passkey Registrierung", 35 | "33": "Mit Benutzername anmelden", 36 | "34": "FIDO-Authentifizierung abgebrochen. Versuchen Sie eine andere Methode.", 37 | "35": "Nicht jetzt ausrollen", 38 | "36": "Offline FIDO", 39 | "37": "Keine passenden Anmeldedaten auf dem Sicherheitsschlüssel." 40 | } 41 | -------------------------------------------------------------------------------- /locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "Nombre de Usuario", 3 | "1": "Contraseña", 4 | "2": "Contraseña anterior", 5 | "3": "Nueva contraseña", 6 | "4": "Confirmar contraseña", 7 | "5": "Iniciar sesión en:", 8 | "6": "Contraseña de un solo uso", 9 | "7": "¡Contraseña de un solo uso incorrecta!", 10 | "8": "Restablecer inicio de sesión", 11 | "9": "Token sin conexión disponible:", 12 | "10": "OTP restantes", 13 | "11": "¡Error de conexión o configuración!\nPor favor compruebe el archivo de registro.", 14 | "12": "Utilice la clave de seguridad", 15 | "13": "Utilice la contraseña de un solo uso", 16 | "14": "PIN de clave de seguridad", 17 | "15": "¡Toca tu llave de seguridad!", 18 | "16": "Conectando a privacyIDEA...", 19 | "17": "Iniciar sesión en privacyIDEA", 20 | "18": "Por favor ingrese su contraseña de un solo uso", 21 | "19": "¡No se encontraron credenciales coincidentes para esta clave de seguridad!", 22 | "20": "¡Conecta tu llave de seguridad!", 23 | "21": "¡Autenticación exitosa! \nComprobando el estado fuera de línea de este token...", 24 | "22": "Recargando token fuera de línea...", 25 | "23": "¡Quite e inserte su dispositivo nuevamente!", 26 | "24": "Falló la comunicación con el dispositivo. Pruebe con otro método o dispositivo de autenticación.", 27 | "25": "¡Pin de llave de seguridad incorrecto!", 28 | "26": "Usar Passkey", 29 | "27": "Ingrese su nombre de usuario", 30 | "28": "Introduce tu contraseña", 31 | "29": "Ingrese su nombre de usuario y contraseña", 32 | "30": "Registro de Passkey. ¡Toca tu llave de seguridad!", 33 | "31": "¡Por favor, ingrese el PIN de su llave de seguridad!", 34 | "32": "Registro de Passkey", 35 | "33": "Iniciar sesión con nombre de usuario", 36 | "34": "Autenticación FIDO cancelada. Pruebe otra forma.", 37 | "35": "No ahora", 38 | "36": "Offline FIDO", 39 | "37": "¡No se encontraron credenciales coincidentes en la llave de seguridad!" 40 | } -------------------------------------------------------------------------------- /WiXSetup/ConfigurationDlgsLocales.wxl: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 28 | This is the privacyIDEA service URL. The protocol can be left out. 29 | The privacyIDEA Credential Provider always uses secure connection. 30 | Example: privacyidea.example.com/service_root/ 31 | 32 | 33 | This is the welcome-message to be displayed in the Windows login page. 34 | 35 | 36 | Note: Leave it empty to keep the default message ("privacyIDEA Logon"). 37 | 38 | 39 | 40 | 41 | The following settings are optional texts and icons to customize the login. 42 | 43 | 44 | The path to a custom 128x128px 24-bit Bitmap (*.bmp) to show on the logon tile. 45 | 46 | -------------------------------------------------------------------------------- /CredentialProviderFilter/CredentialProviderFilter.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Resource Files 37 | 38 | 39 | 40 | 41 | Resource Files 42 | 43 | 44 | 45 | 46 | Source Files 47 | 48 | 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The privacyIDEA Credential Provider adds multifactor authentication to the Windows Desktop or Server login. 2 | 3 | The Credential Provider communicates with the privacyIDEA authentication system via REST API. 4 | 5 | [privacyIDEA is an open source two factor authentication system](https://github.com/privacyidea/privacyidea) 6 | 7 | ### Features 8 | * FIDO Authentication with Passkey/WebAuthn 9 | - Usernameless 10 | - Offline 11 | - With RDP 12 | * Push Token with the [privacyIDEA Authenticator App](https://github.com/privacyidea/pi-authenticator) 13 | * OTP Token like HOTP, TOTP, Email or SMS 14 | * Configurable usage depending on scenario (Logon, Unlock with RDP or local) 15 | * Fallback/recovery options 16 | - Excluded Account 17 | - Excluded Group 18 | - Fallback URL 19 | * Configurable texts 20 | 21 | ### Test Version and Enterprise Support 22 | If you just want to test the software, an MSI is available in the release section as well as a test subscription. 23 | 24 | [Enterprise Support and an extended Subscription](https://netknights.it/en/produkte/privacyidea-credential-provider/) is provided by NetKnights, who also advance the development of this project and privacyIDEA. 25 | 26 | ### Documentation 27 | The documentation can be found in ``/doc``, most notably the [configuration options](https://github.com/privacyidea/privacyidea-credential-provider/blob/master/doc/configuration.rst). 28 | 29 | The complete documentation can be found at [readthedocs.io](https://privacyidea-credential-provider.readthedocs.io/en/latest/index.html). 30 | 31 | ### Dependencies 32 | This project requires [json.hpp](https://github.com/nlohmann/json) in ``CppClient/nlohmann/json.hpp``. 33 | It also requires [libfido2](https://developers.yubico.com/libfido2/Releases/) for Windows to be in the ``$SolutionDir$`` (or adjust the include settings). 34 | Supports libfido2 with PCSC enabled. 35 | 36 | To build the installer, the VC143 merge modules are required to be in ``lib/merge``. 37 | -------------------------------------------------------------------------------- /CppClient/CppClient/OfflineData.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2019 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | #pragma once 20 | 21 | #include "Logger.h" 22 | #include 23 | 24 | class OfflineData 25 | { 26 | public: 27 | int GetLowestKey() 28 | { 29 | int lowestKey = INT_MAX; 30 | 31 | for (auto& item : offlineOTPs) 32 | { 33 | try 34 | { 35 | const int key = stoi(item.first); 36 | lowestKey = (lowestKey > key ? key : lowestKey); 37 | } 38 | catch (const std::invalid_argument& e) 39 | { 40 | PIDebug(e.what()); 41 | } 42 | } 43 | 44 | return lowestKey; 45 | } 46 | 47 | bool operator==(const OfflineData& other) const noexcept 48 | { 49 | return username == other.username && serial == other.serial && refilltoken == other.refilltoken; 50 | } 51 | 52 | std::string username = ""; 53 | std::string serial = ""; 54 | std::string refilltoken = ""; 55 | 56 | // HOTP 57 | std::map offlineOTPs; 58 | int rounds = 10000; 59 | int count = 0; // Max OTPs that will be stored offline 60 | 61 | // WebAuthn 62 | std::string pubKey; 63 | std::string credId; 64 | std::string rpId; 65 | std::string userId; 66 | 67 | bool isWebAuthn() const noexcept { return !pubKey.empty() && !credId.empty() && !rpId.empty(); } 68 | }; 69 | -------------------------------------------------------------------------------- /CppClient/CppClient/Endpoint.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2019 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #pragma once 21 | 22 | #include "Challenge.h" 23 | #include "PIConfig.h" 24 | #include 25 | #include 26 | 27 | #define PI_ERROR_SERVER_UNAVAILABLE ((HRESULT)0x88809014) 28 | #define PI_ERROR_ENDPOINT_SETUP ((HRESULT)0x88809015) 29 | 30 | enum class RequestMethod 31 | { 32 | GET, 33 | POST 34 | }; 35 | 36 | class Endpoint 37 | { 38 | public: 39 | Endpoint(PIConfig config) : _config(config), hostname(config.hostname), path(config.path), port(config.port) {}; 40 | 41 | std::string SendRequest( 42 | const std::string& endpoint, 43 | const std::map& parameters, 44 | const std::map& headers = std::map(), 45 | const RequestMethod& method = RequestMethod::POST); 46 | 47 | HRESULT GetLastErrorCode(); 48 | 49 | std::wstring hostname; 50 | std::wstring path; 51 | int port = 0; 52 | 53 | private: 54 | 55 | std::string EncodeRequestParameters(const std::map& parameters); 56 | 57 | std::wstring EncodeUTF16(const std::string& str, int codepage); 58 | 59 | std::string URLEncode(const std::string& in); 60 | 61 | HRESULT _lastErrorCode = 0; 62 | 63 | PIConfig _config; 64 | }; 65 | 66 | -------------------------------------------------------------------------------- /CppClient/CppClient/PIConfig.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | #pragma once 20 | #include 21 | #include 22 | 23 | /// 24 | /// This is a subset of the configuration loaded by the application using the cpp-client. 25 | /// These values are required for the operation of the cpp-client. 26 | /// 27 | struct PIConfig 28 | { 29 | std::wstring hostname = L""; 30 | std::wstring path = L""; 31 | int port = 0; 32 | 33 | std::wstring fallbackHostname = L""; 34 | std::wstring fallbackPath = L""; 35 | int fallbackPort = 0; 36 | 37 | bool ignoreInvalidCN = false; 38 | bool ignoreUnknownCA = false; 39 | std::wstring userAgent = L"privacyidea-cpp-client"; 40 | 41 | std::map realmMap = std::map(); 42 | std::wstring defaultRealm = L""; 43 | bool logPasswords = false; 44 | std::wstring offlineFilePath = L"C:\\offlineFile.json"; 45 | int offlineTryWindow = 10; 46 | bool sendUPN = false; 47 | 48 | // optionals 49 | int resolveTimeout = 0; // = infinite 50 | int connectTimeout = 60000; 51 | int sendTimeout = 30000; 52 | int receiveTimeout = 30000; 53 | 54 | // Can be "system" or a valid language code like "en-US" or "de-DE" 55 | // If format is wrong, use system 56 | std::string acceptLanguage = "system"; 57 | }; 58 | -------------------------------------------------------------------------------- /CppClient/CppClient/CppClient.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | FIDO 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | FIDO 32 | 33 | 34 | FIDO 35 | 36 | 37 | FIDO 38 | 39 | 40 | FIDO 41 | 42 | 43 | FIDO 44 | 45 | 46 | FIDO 47 | 48 | 49 | 50 | 51 | {fe3f4fa5-c3aa-4300-808b-d570ee577685} 52 | 53 | 54 | -------------------------------------------------------------------------------- /CredentialProvider/Translator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "RegistryReader.h" 3 | #include 4 | #include 5 | 6 | #define PITranslate(messageId) Translator::GetInstance().Translate(messageId) 7 | 8 | // 9 | // Singleton Translator class that reads language files and use translation methods. 10 | // Sets the current language and loads the locales from file. 11 | // The language file location is in the registry key localesPath 12 | // By default, uses GetUserLocale() to get the current language from Windows. 13 | // Example: es-AR or es_AR. 14 | // First looks up for language-region "es_AR", if the file is not found then looks for language "es", and if not found falls back to "en" (english) 15 | // The method Translate(textId), returns the wstring corresponding to the id in the current language. 16 | // 17 | 18 | class Translator final { 19 | public: 20 | Translator(const Translator&) = delete; 21 | void operator=(const Translator&) = delete; 22 | 23 | Translator(Translator&&) noexcept = delete; 24 | Translator& operator=(Translator&&) noexcept = delete; 25 | 26 | ~Translator() = default; 27 | 28 | static Translator& GetInstance() { 29 | static Translator instance; 30 | return instance; 31 | } 32 | 33 | void SetLanguage(const std::string& language); 34 | std::wstring Translate(int textId); // Translate the textId to the corresponding current language 35 | std::string GetLanguage(); // Returns current language 36 | std::string GetRegion(); // Returns current region 37 | std::string GetUserLocale(); 38 | 39 | private: 40 | Translator(); 41 | 42 | static std::unordered_map _translations; 43 | static std::string _currentLanguage; 44 | static std::string _currentRegion; 45 | static std::wstring _localesPath; 46 | 47 | bool TryLoadTranslations(const std::string& language, const std::string& region = ""); 48 | bool LoadTranslations(const std::string& locale); 49 | 50 | std::string GetLanguageFromLocale(const std::string& locale); 51 | std::string GetRegionFromLocale(const std::string& locale); 52 | }; 53 | -------------------------------------------------------------------------------- /CppClient/CppClient/Logger.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2019 Nils Behlen 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | ** 17 | ** * * * * * * * * * * * * * * * * * * */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) 26 | 27 | #define PIError(message) Logger::Get().Log(message, __FILENAME__, __LINE__, false) 28 | #define PIDebug(message) Logger::Get().Log(message, __FILENAME__, __LINE__, true) 29 | 30 | // Singleton logger class that writes to a file on C: and to OutputDebugString 31 | class Logger 32 | { 33 | public: 34 | Logger(Logger const&) = delete; 35 | void operator=(Logger const&) = delete; 36 | 37 | static Logger& Get() 38 | { 39 | static Logger instance; 40 | return instance; 41 | } 42 | 43 | void Log(const char* message, const char* file, int line, bool isDebugMessage); 44 | 45 | void Log(const wchar_t* message, const char* file, int line, bool isDebugMessage); 46 | 47 | void Log(const long message, const char* file, int line, bool isDebugMessage); 48 | 49 | void Log(const std::string& message, const char* file, int line, bool isDebugMessage); 50 | 51 | void Log(const std::wstring& message, const char* file, int line, bool isDebugMessage); 52 | 53 | bool logDebug = false; 54 | 55 | private: 56 | std::string logfilePath = "C:\\PICredentialProviderLog.txt"; 57 | 58 | Logger() = default; 59 | 60 | std::ofstream _logStream; 61 | std::mutex _mutex; 62 | 63 | void LogS(const std::string& message, const char* file, int line, bool isDebugMessage); 64 | 65 | void LogW(const std::wstring& message, const char* file, int line, bool isDebugMessage); 66 | }; 67 | -------------------------------------------------------------------------------- /CppClient/CppClient/Cpp Client.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | -------------------------------------------------------------------------------- /WiXSetup/Config.wxi: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /CppClient/CppClient/PIResponse.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #pragma once 21 | #include "Challenge.h" 22 | #include "FIDORegistrationRequest.h" 23 | #include "FIDOSignRequest.h" 24 | #include "AuthenticationStatus.h" 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | class PIResponse 31 | { 32 | public: 33 | bool status = false; 34 | bool value = false; 35 | AuthenticationStatus authenticationStatus = AuthenticationStatus::NOT_SET; 36 | std::string transactionId; 37 | std::string message; 38 | 39 | std::string errorMessage; 40 | int errorCode = 0; 41 | 42 | std::vector challenges; 43 | 44 | bool IsPushAvailable(); 45 | 46 | bool isAuthenticationSuccessful() const; 47 | 48 | std::string GetPushMessage(); 49 | 50 | std::optional GetFIDOSignRequest(); 51 | 52 | std::string GetFIDOMessage(); 53 | 54 | std::string GetNonFIDOMessage(); // everything except FIDO token 55 | 56 | std::string preferredMode; 57 | 58 | std::optional username = std::nullopt; 59 | 60 | std::optional passkeyRegistration = std::nullopt; 61 | 62 | std::optional passkeyChallenge = std::nullopt; 63 | 64 | bool IsVersionHigherOrEqual(int major, int minor = 0, int patch = 0) const; 65 | 66 | int privacyIDEAVersionMajor = 99; 67 | int privacyIDEAVersionMinor = 99; 68 | int privacyIDEAVersionPatch = 99; 69 | std::string privacyIDEAVersionSuffix = ""; // like dev0, beta1 70 | 71 | bool isEnrollViaMultichallenge = false; // true if the response is a multichallenge response, e.g. for FIDO2 registration 72 | bool isEnrollCancellable = false; // true if the enrollment can be cancelled by the user 73 | }; 74 | 75 | -------------------------------------------------------------------------------- /CredentialProviderFilter/CCredentialProviderFilter.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 2017 NetKnights GmbH 5 | ** 6 | ** Author Dominik Pretzsch 7 | ** Nils Behlen 8 | ** 9 | ** Licensed under the Apache License, Version 2.0 (the "License"); 10 | ** you may not use this file except in compliance with the License. 11 | ** You may obtain a copy of the License at 12 | ** 13 | ** http://www.apache.org/licenses/LICENSE-2.0 14 | ** 15 | ** Unless required by applicable law or agreed to in writing, software 16 | ** distributed under the License is distributed on an "AS IS" BASIS, 17 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | ** See the License for the specific language governing permissions and 19 | ** limitations under the License. 20 | ** 21 | ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include "Dll.h" 27 | #include "resource.h" 28 | 29 | class CCredentialProviderFilter : public ICredentialProviderFilter 30 | { 31 | public: 32 | //This section contains some COM boilerplate code 33 | 34 | // IUnknown 35 | STDMETHOD_(ULONG, AddRef)() 36 | { 37 | return _cRef++; 38 | } 39 | 40 | STDMETHOD_(ULONG, Release)() 41 | { 42 | LONG cRef = _cRef--; 43 | if (!cRef) 44 | { 45 | delete this; 46 | } 47 | return cRef; 48 | } 49 | 50 | STDMETHOD (QueryInterface)(REFIID riid, void** ppv) 51 | { 52 | HRESULT hr; 53 | if (IID_IUnknown == riid || IID_ICredentialProviderFilter == riid) 54 | { 55 | *ppv = this; 56 | reinterpret_cast(*ppv)->AddRef(); 57 | hr = S_OK; 58 | } 59 | else 60 | { 61 | *ppv = NULL; 62 | hr = E_NOINTERFACE; 63 | } 64 | return hr; 65 | } 66 | //#pragma warning(disable:4100) 67 | 68 | public: 69 | //Implementation of ICredentialProviderFilter 70 | IFACEMETHODIMP Filter(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, 71 | DWORD dwFlags, 72 | GUID* rgclsidProviders, 73 | BOOL* rgbAllow, 74 | DWORD cProviders); 75 | 76 | IFACEMETHODIMP UpdateRemoteCredential(const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpcsIn, 77 | CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpcsOut); 78 | 79 | friend HRESULT CSample_CreateInstance(__in REFIID riid, __deref_out void** ppv); 80 | 81 | protected: 82 | CCredentialProviderFilter(); 83 | __override ~CCredentialProviderFilter(); 84 | 85 | private: 86 | LONG _cRef; 87 | 88 | private: 89 | bool _filterEnabled = false; 90 | }; -------------------------------------------------------------------------------- /CppClient/CppClient/Convert.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2024 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #pragma once 21 | #include 22 | #include 23 | #include 24 | 25 | class Convert 26 | { 27 | public: 28 | static std::wstring ToWString(const std::string& s); 29 | static std::string ToString(const std::wstring& ws); 30 | static std::string ToString(const bool b); 31 | static std::wstring ToUpperCase(std::wstring s); 32 | static std::string ToUpperCase(std::string s); 33 | static std::string LongToHexString(long in); 34 | static std::wstring JoinW(const std::vector& elements, const wchar_t* separator); 35 | 36 | static std::vector Base64Decode(const std::string& base64String); 37 | static std::vector Base64URLDecode(const std::string& base64String); 38 | static std::string Base64Encode(const unsigned char* data, const size_t size, bool padded = false); 39 | static std::string Base64Encode(const std::vector& data, bool padded = false); 40 | static std::string Base64URLEncode(const unsigned char* data, const size_t size, bool padded = false); 41 | static std::string Base64URLEncode(const std::vector& data, bool padded = false); 42 | 43 | static char* UnicodeToCodePage(int codePage, const wchar_t* src); 44 | // replace '+' with '-' and '/' with '_' 45 | static void Base64ToBase64URL(std::string& base64); 46 | // replace '-' with '+' and '_' with '/' 47 | static void Base64URLToBase64(std::string& base64URL); 48 | 49 | // Replaces '.' with '+'. 50 | static void Base64ToABase64(std::string& base64); 51 | 52 | static std::string BytesToHex(const unsigned char* data, const size_t dataSize); 53 | static std::string BytesToHex(std::vector bytes); 54 | 55 | static std::vector HexToBytes(const std::string& hexString); 56 | 57 | static std::string ReplaceAll(const std::string& input, const std::string& target, const std::string& replacement); 58 | }; 59 | -------------------------------------------------------------------------------- /CppClient/CppClient/FIDODevice.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #pragma once 21 | #include "FIDOSignRequest.h" 22 | #include "FIDOSignResponse.h" 23 | #include "OfflineData.h" 24 | #include "FIDORegistrationRequest.h" 25 | #include "FIDORegistrationResponse.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | constexpr auto fidoFlags = FIDO_DISABLE_U2F_FALLBACK | FIDO_DEBUG; 32 | 33 | constexpr auto FIDO_DEVICE_ERR_TX = 0x88809089; 34 | 35 | constexpr auto OFFLINE_CHALLENGE_SIZE = 64; 36 | 37 | class FIDODevice 38 | { 39 | public: 40 | static std::vector GetDevices(bool filterWindowsHello = true, bool log = true); 41 | 42 | FIDODevice(const fido_dev_info_t* devinfo, bool log = true); 43 | FIDODevice() = default; 44 | 45 | int Sign( 46 | const FIDOSignRequest& signRequest, 47 | const std::string& origin, 48 | const std::string& pin, 49 | FIDOSignResponse& signResponse) const; 50 | 51 | int SignAndVerifyAssertion( 52 | const std::vector& offlineData, 53 | const std::string& origin, 54 | const std::string& pin, 55 | std::string& serialUsed) const; 56 | 57 | std::optional Register( 58 | const FIDORegistrationRequest& registration, 59 | const std::string& pin); 60 | 61 | std::string GetPath() const { return _path; } 62 | std::string GetManufacturer() const { return _manufacturer; } 63 | std::string GetProduct() const { return _product; } 64 | bool HasPin() const noexcept { return _hasPin; } 65 | bool IsWinHello() const noexcept { return _isWinHello; } 66 | bool HasUV() const noexcept { return _hasUV; } 67 | 68 | static std::string GenerateRandomAsBase64URL(long size); 69 | 70 | std::string ToString() const; 71 | 72 | std::vector GetRpIds(std::string pin) const; 73 | 74 | std::vector GetUsersForRpId(std::string pin, std::string rpId) const; 75 | 76 | private: 77 | int GetDeviceInfo(); 78 | 79 | std::string BuildAttestationObject(fido_cred_t* cred); 80 | 81 | std::string _path; 82 | std::string _manufacturer; 83 | std::string _product; 84 | bool _hasPin = false; 85 | bool _isWinHello = false; 86 | bool _hasUV = false; 87 | std::vector _supportedAlgorithms; 88 | long _remainingResidentKeys = -1; 89 | bool _newPinRequired = false; 90 | }; 91 | -------------------------------------------------------------------------------- /CredentialProviderFilter/resources.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include 11 | #include "version.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // German (Germany) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) 20 | LANGUAGE LANG_GERMAN, SUBLANG_GERMAN 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 29 | 1 TEXTINCLUDE 30 | BEGIN 31 | "resource.h\0" 32 | END 33 | 34 | 2 TEXTINCLUDE 35 | BEGIN 36 | "#include ""afxres.h""\r\n" 37 | "#include ""version.h""\r\n" 38 | "\0" 39 | END 40 | 41 | 3 TEXTINCLUDE 42 | BEGIN 43 | "\r\n" 44 | "\0" 45 | END 46 | 47 | #endif // APSTUDIO_INVOKED 48 | 49 | 50 | ///////////////////////////////////////////////////////////////////////////// 51 | // 52 | // Version 53 | // 54 | 55 | VS_VERSION_INFO VERSIONINFO 56 | FILEVERSION VERSION_MAJOR , VERSION_MINOR , VERSION_BUILD, VERSION_REVISION 57 | PRODUCTVERSION VERSION_MAJOR , VERSION_MINOR , VERSION_BUILD, VERSION_REVISION 58 | FILEFLAGSMASK 0x3fL 59 | #ifdef _DEBUG 60 | FILEFLAGS 0x1L 61 | #else 62 | FILEFLAGS 0x0L 63 | #endif 64 | FILEOS 0x40004L 65 | FILETYPE 0x0L 66 | FILESUBTYPE 0x0L 67 | BEGIN 68 | BLOCK "StringFileInfo" 69 | BEGIN 70 | BLOCK "040904b0" 71 | BEGIN 72 | VALUE "CompanyName", "Netknights GmbH" 73 | VALUE "FileDescription", "CredentialProviderFilter for the privacyIDEA CredentialProvider for Windows logon" 74 | VALUE "FileVersion", VER_FILE_VERSION_STR 75 | VALUE "InternalName", "privacyIDEACredentialProviderFilter.dll" 76 | VALUE "LegalCopyright", "Copyright (C) 2025 NetKnights, 2016 Last Squirrel IT" 77 | VALUE "OriginalFilename", "privacyIDEACredentialProviderFilter.dll" 78 | VALUE "ProductName", "privacyIDEACredentialProvider" 79 | VALUE "ProductVersion", VER_FILE_VERSION_STR 80 | END 81 | END 82 | BLOCK "VarFileInfo" 83 | BEGIN 84 | VALUE "Translation", 0x409, 1200 85 | END 86 | END 87 | 88 | #endif // German (Germany) resources 89 | ///////////////////////////////////////////////////////////////////////////// 90 | 91 | 92 | 93 | #ifndef APSTUDIO_INVOKED 94 | ///////////////////////////////////////////////////////////////////////////// 95 | // 96 | // Generated from the TEXTINCLUDE 3 resource. 97 | // 98 | 99 | 100 | ///////////////////////////////////////////////////////////////////////////// 101 | #endif // not APSTUDIO_INVOKED 102 | 103 | -------------------------------------------------------------------------------- /CppClient/CppClient/JsonParser.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2024 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #pragma once 21 | #include "PIResponse.h" 22 | #include "OfflineData.h" 23 | #include 24 | #include 25 | #include 26 | 27 | #define PI_JSON_PARSE_ERROR ((HRESULT)0x88809031) 28 | constexpr auto JSON_DUMP_INDENTATION = 4; 29 | 30 | class JsonParser 31 | { 32 | public: 33 | /// 34 | /// Parse the contents of a privacyIDEA response into an object. 35 | /// 36 | /// 37 | /// 38 | /// 39 | /// S_OK success, 40 | /// PI_JSON_PARSE_ERROR if the input is malformed or a required field is missing 41 | /// 42 | HRESULT ParseResponse(std::string serverResponse, PIResponse &response); 43 | 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | std::vector ParseResponseForOfflineData(std::string input); 50 | 51 | /// 52 | /// The format of the saved file differs from the server response. Therefore it should be parsed with this method. 53 | /// 54 | /// 55 | /// 56 | std::vector ParseFileContentsForOfflineData(std::string input); 57 | 58 | HRESULT ParseOfflineDataItemFromString(std::string input, OfflineData& data); 59 | 60 | std::string OfflineDataToString(std::vector data); 61 | 62 | bool ParsePollTransaction(std::string input); 63 | 64 | HRESULT ParseRefillResponse(const std::string& in, const std::string& username, OfflineData& data); 65 | 66 | std::string GetRefilltoken(std::string input); 67 | 68 | 69 | // Return the input json with indentation of 4. If the input is not a valid json it is returned as is. 70 | static std::string PrettyFormatJson(std::string input); 71 | 72 | /// 73 | /// Check if result->error->code is 905. 74 | /// If that is the case, the token has been unmarked from being used offline and this function returns false. 75 | /// If there is an error, return true to keep the token. 76 | /// 77 | /// 78 | /// true if still marked for offline or error, false if not 79 | bool IsStillActiveOfflineToken(const std::string& input); 80 | }; 81 | 82 | -------------------------------------------------------------------------------- /WiXSetup/WiXSetup.wixproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x86 7 | 3.7 8 | 4d2113b2-c2d3-44d1-bfc7-5502e801872e 9 | 2.0 10 | privacyIDEACredentialProviderSetup 11 | Package 12 | $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets 13 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets 14 | CredentialProviderSetup 15 | bin\$(Platform)\$(Configuration)\ 16 | obj\$(Platform)\$(Configuration)\ 17 | 18 | 19 | 20 | Debug 21 | ICE09; 22 | 23 | 24 | 25 | ICE09; 26 | False 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | $(WixExtDir)\WixUIExtension.dll 38 | WixUIExtension 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | CredentialProviderFilter 47 | {e8100bb4-e5f1-47d0-a4fd-4a8c70503d9f} 48 | True 49 | True 50 | Binaries;Content;Satellites 51 | INSTALLFOLDER 52 | 53 | 54 | CredentialProvider 55 | {2df895c3-d1b4-4632-8f76-f06670a0d311} 56 | True 57 | True 58 | Binaries;Content;Satellites 59 | INSTALLFOLDER 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /CredentialProvider/RCa03772: -------------------------------------------------------------------------------- 1 | #line 1"C:\\Users\\Nils\\Desktop\\privacyidea-credentialprovider-git2\\CredentialProvider\\Resource.rc" 2 | #line 1 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource1.h" 6 | #line 5 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "winres.h" 13 | #line 12 14 | ///////////////////////////////////////////////////////////////////////////// 15 | #undef APSTUDIO_READONLY_SYMBOLS 16 | #line 15 17 | ///////////////////////////////////////////////////////////////////////////// 18 | // Englisch (Vereinigte Staaten) resources 19 | #line 18 20 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) 21 | LANGUAGE 7, 1 22 | #line 21 23 | #ifdef APSTUDIO_INVOKED 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // 26 | // TEXTINCLUDE 27 | // 28 | #line 27 29 | 1 TEXTINCLUDE 30 | BEGIN 31 | "resource1.h\0" 32 | END 33 | #line 32 34 | 2 TEXTINCLUDE 35 | BEGIN 36 | "#include ""winres.h""\r\n" 37 | "\0" 38 | END 39 | #line 38 40 | 3 TEXTINCLUDE 41 | BEGIN 42 | "\r\n" 43 | "\0" 44 | END 45 | #line 44 46 | #endif // APSTUDIO_INVOKED 47 | #line 46 48 | #endif // Englisch (Vereinigte Staaten) resources 49 | ///////////////////////////////////////////////////////////////////////////// 50 | #line 51 51 | #ifndef APSTUDIO_INVOKED 52 | ///////////////////////////////////////////////////////////////////////////// 53 | // 54 | // Generated from the TEXTINCLUDE 3 resource. 55 | // 56 | #line 58 57 | ///////////////////////////////////////////////////////////////////////////// 58 | #endif // not APSTUDIO_INVOKED 59 | -------------------------------------------------------------------------------- /CredentialProvider/RCb03772: -------------------------------------------------------------------------------- 1 | #line 1"C:\\Users\\Nils\\Desktop\\privacyidea-credentialprovider-git2\\CredentialProvider\\Resource.rc" 2 | #line 1 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource1.h" 6 | #line 5 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "winres.h" 13 | #line 12 14 | ///////////////////////////////////////////////////////////////////////////// 15 | #undef APSTUDIO_READONLY_SYMBOLS 16 | #line 15 17 | ///////////////////////////////////////////////////////////////////////////// 18 | // Englisch (Vereinigte Staaten) resources 19 | #line 18 20 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) 21 | LANGUAGE 7, 1 22 | #line 21 23 | #ifdef APSTUDIO_INVOKED 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // 26 | // TEXTINCLUDE 27 | // 28 | #line 27 29 | 1 TEXTINCLUDE 30 | BEGIN 31 | "resource1.h\0" 32 | END 33 | #line 32 34 | 2 TEXTINCLUDE 35 | BEGIN 36 | "#include ""winres.h""\r\n" 37 | "\0" 38 | END 39 | #line 38 40 | 3 TEXTINCLUDE 41 | BEGIN 42 | "\r\n" 43 | "\0" 44 | END 45 | #line 44 46 | #endif // APSTUDIO_INVOKED 47 | #line 46 48 | #endif // Englisch (Vereinigte Staaten) resources 49 | ///////////////////////////////////////////////////////////////////////////// 50 | #line 51 51 | #ifndef APSTUDIO_INVOKED 52 | ///////////////////////////////////////////////////////////////////////////// 53 | // 54 | // Generated from the TEXTINCLUDE 3 resource. 55 | // 56 | #line 58 57 | ///////////////////////////////////////////////////////////////////////////// 58 | #endif // not APSTUDIO_INVOKED 59 | -------------------------------------------------------------------------------- /CredentialProvider/RCa09068: -------------------------------------------------------------------------------- 1 | #line 1"C:\\Users\\Nils\\Desktop\\privacyidea-credentialprovider-git2\\CredentialProvider\\Resource2.rc" 2 | #line 1 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource3.h" 6 | #line 5 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "winres.h" 13 | #line 12 14 | ///////////////////////////////////////////////////////////////////////////// 15 | #undef APSTUDIO_READONLY_SYMBOLS 16 | #line 15 17 | ///////////////////////////////////////////////////////////////////////////// 18 | // Englisch (Vereinigte Staaten) resources 19 | #line 18 20 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) 21 | LANGUAGE 7, 1 22 | #line 21 23 | #ifdef APSTUDIO_INVOKED 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // 26 | // TEXTINCLUDE 27 | // 28 | #line 27 29 | 1 TEXTINCLUDE 30 | BEGIN 31 | "resource3.h\0" 32 | END 33 | #line 32 34 | 2 TEXTINCLUDE 35 | BEGIN 36 | "#include ""winres.h""\r\n" 37 | "\0" 38 | END 39 | #line 38 40 | 3 TEXTINCLUDE 41 | BEGIN 42 | "\r\n" 43 | "\0" 44 | END 45 | #line 44 46 | #endif // APSTUDIO_INVOKED 47 | #line 46 48 | #endif // Englisch (Vereinigte Staaten) resources 49 | ///////////////////////////////////////////////////////////////////////////// 50 | #line 51 51 | #ifndef APSTUDIO_INVOKED 52 | ///////////////////////////////////////////////////////////////////////////// 53 | // 54 | // Generated from the TEXTINCLUDE 3 resource. 55 | // 56 | #line 58 57 | ///////////////////////////////////////////////////////////////////////////// 58 | #endif // not APSTUDIO_INVOKED 59 | -------------------------------------------------------------------------------- /CredentialProvider/RCa11524: -------------------------------------------------------------------------------- 1 | #line 1"C:\\Users\\Nils\\Desktop\\privacyidea-credentialprovider-git2\\CredentialProvider\\Resource1.rc" 2 | #line 1 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource2.h" 6 | #line 5 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "winres.h" 13 | #line 12 14 | ///////////////////////////////////////////////////////////////////////////// 15 | #undef APSTUDIO_READONLY_SYMBOLS 16 | #line 15 17 | ///////////////////////////////////////////////////////////////////////////// 18 | // Englisch (Vereinigte Staaten) resources 19 | #line 18 20 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) 21 | LANGUAGE 7, 1 22 | #line 21 23 | #ifdef APSTUDIO_INVOKED 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // 26 | // TEXTINCLUDE 27 | // 28 | #line 27 29 | 1 TEXTINCLUDE 30 | BEGIN 31 | "resource2.h\0" 32 | END 33 | #line 32 34 | 2 TEXTINCLUDE 35 | BEGIN 36 | "#include ""winres.h""\r\n" 37 | "\0" 38 | END 39 | #line 38 40 | 3 TEXTINCLUDE 41 | BEGIN 42 | "\r\n" 43 | "\0" 44 | END 45 | #line 44 46 | #endif // APSTUDIO_INVOKED 47 | #line 46 48 | #endif // Englisch (Vereinigte Staaten) resources 49 | ///////////////////////////////////////////////////////////////////////////// 50 | #line 51 51 | #ifndef APSTUDIO_INVOKED 52 | ///////////////////////////////////////////////////////////////////////////// 53 | // 54 | // Generated from the TEXTINCLUDE 3 resource. 55 | // 56 | #line 58 57 | ///////////////////////////////////////////////////////////////////////////// 58 | #endif // not APSTUDIO_INVOKED 59 | -------------------------------------------------------------------------------- /CppClient/CppClient/OfflineHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* * * * * * * * * * * * * * * * * * * * * 3 | ** 4 | ** Copyright 2019 NetKnights GmbH 5 | ** Author: Nils Behlen 6 | ** 7 | ** Licensed under the Apache License, Version 2.0 (the "License"); 8 | ** you may not use this file except in compliance with the License. 9 | ** You may obtain a copy of the License at 10 | ** 11 | ** http://www.apache.org/licenses/LICENSE-2.0 12 | ** 13 | ** Unless required by applicable law or agreed to in writing, software 14 | ** distributed under the License is distributed on an "AS IS" BASIS, 15 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ** See the License for the specific language governing permissions and 17 | ** limitations under the License. 18 | ** 19 | ** * * * * * * * * * * * * * * * * * * */ 20 | 21 | #include "OfflineData.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // 888090-2X OFFLINE 28 | #define PI_OFFLINE_DATA_NO_OTPS_LEFT ((HRESULT)0x88809020) 29 | #define PI_OFFLINE_NO_OFFLINE_DATA ((HRESULT)0x88809022) 30 | #define PI_OFFLINE_FILE_DOES_NOT_EXIST ((HRESULT)0x88809023) 31 | #define PI_OFFLINE_FILE_EMPTY ((HRESULT)0x88809024) 32 | #define PI_OFFLINE_WRONG_OTP ((HRESULT)0x88809025) 33 | 34 | class OfflineHandler 35 | { 36 | public: 37 | OfflineHandler(const std::wstring& filePath, int tryWindow = 10); 38 | 39 | ~OfflineHandler(); 40 | 41 | /// 42 | /// Check if the given OTP matches with one of the offline OTPs in the configured window. 43 | /// If the given OTP is not the first in the list, the values between the start of the list and the matching position are removed. 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | HRESULT VerifyOfflineOTP(const std::wstring& otp, const std::string& username, std::string& serialUsed); 50 | 51 | HRESULT GetRefillToken(const std::string& username, const std::string& serial, std::string& refilltoken); 52 | 53 | HRESULT AddOfflineData(const OfflineData& data); 54 | 55 | /// 56 | /// Get the number of remaining offline OTPs for the user. 57 | /// 58 | /// 59 | /// The number of remaining offline OTP values or 0 if no data is found 60 | size_t GetOfflineOTPCount(const std::string& username, const std::string& serial); 61 | 62 | std::vector> GetTokenInfo(const std::string& username); 63 | 64 | std::vector GetFIDODataFor(const std::string& username); 65 | 66 | std::vector GetAllFIDOData(); 67 | 68 | bool RemoveOfflineData(const std::string& username, const std::string& serial); 69 | 70 | bool UpdateRefilltoken(std::string serial, std::string refilltoken); 71 | 72 | std::optional GetUsernameForSerial(const std::string& serial); 73 | 74 | private: 75 | std::vector _dataSets = std::vector(); 76 | 77 | std::wstring _filePath = L"C:\\offlineFile.json"; 78 | 79 | int _tryWindow = 10; 80 | 81 | bool PBKDF2SHA512Verify(std::wstring password, std::string storedValue); 82 | 83 | std::string GetNextValue(std::string& in); 84 | 85 | HRESULT SaveToFile(); 86 | 87 | HRESULT LoadFromFile(); 88 | }; 89 | 90 | -------------------------------------------------------------------------------- /CppClient/CppClient/Logger.cpp: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 21 | 22 | #include "Logger.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | using namespace std; 29 | 30 | void Logger::LogS(const string& message, const char* file, int line, bool isDebugMessage) 31 | { 32 | // Do not log debug messages if it is not enabled 33 | if (!logDebug && isDebugMessage) 34 | { 35 | return; 36 | } 37 | 38 | // Format: [Time] [file:line] message 39 | time_t rawtime = NULL; 40 | struct tm* timeinfo = (tm*)CoTaskMemAlloc(sizeof(tm)); 41 | char buffer[80]; 42 | SecureZeroMemory(buffer, sizeof(buffer)); 43 | if (timeinfo == nullptr) 44 | { 45 | return; 46 | } 47 | time(&rawtime); 48 | const errno_t err = localtime_s(timeinfo, &rawtime); 49 | if (err != 0) 50 | { 51 | return; 52 | } 53 | strftime(buffer, sizeof(buffer), "%d-%m-%Y %H:%M:%S", timeinfo); 54 | CoTaskMemFree(timeinfo); 55 | string fullMessage = "[" + string(buffer) + "] [" + string(file) + ":" + to_string(line) + "] " + message; 56 | 57 | ofstream os; 58 | os.open(logfilePath.c_str(), std::ios_base::app); 59 | os << fullMessage << endl; 60 | 61 | #ifndef _OUTPUT_TO_COUT 62 | OutputDebugStringA(fullMessage.c_str()); 63 | OutputDebugStringA("\n"); 64 | #else 65 | //std::cout << fullMessage << std::endl; 66 | #endif // !_OUTPUT_TO_COUT 67 | } 68 | 69 | void Logger::LogW(const wstring& message, const char* file, int line, bool isDebugMessage) 70 | { 71 | using convert_typeX = std::codecvt_utf8; 72 | std::wstring_convert converterX; 73 | 74 | string conv = converterX.to_bytes(message); 75 | LogS(conv, file, line, isDebugMessage); 76 | } 77 | 78 | void Logger::Log(const char* message, const char* file, int line, bool isDebugMessage) 79 | { 80 | string msg = ""; 81 | if (message != nullptr && message[0] != NULL) 82 | { 83 | msg = string(message); 84 | } 85 | LogS(msg, file, line, isDebugMessage); 86 | } 87 | 88 | void Logger::Log(const wchar_t* message, const char* file, int line, bool isDebugMessage) 89 | { 90 | wstring msg = L""; 91 | if (message != nullptr && message[0] != NULL) 92 | { 93 | msg = wstring(message); 94 | } 95 | LogW(msg, file, line, isDebugMessage); 96 | } 97 | 98 | void Logger::Log(const long message, const char* file, int line, bool isDebugMessage) 99 | { 100 | string i = "(long) " + to_string(message); 101 | LogS(i, file, line, isDebugMessage); 102 | } 103 | 104 | void Logger::Log(const std::string& message, const char* file, int line, bool isDebugMessage) 105 | { 106 | LogS(message, file, line, isDebugMessage); 107 | } 108 | 109 | void Logger::Log(const std::wstring& message, const char* file, int line, bool isDebugMessage) 110 | { 111 | LogW(message, file, line, isDebugMessage); 112 | } 113 | -------------------------------------------------------------------------------- /CredentialProvider/core/CProvider.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 2017 NetKnights GmbH 5 | ** 6 | ** Author Dominik Pretzsch 7 | ** Nils Behlen 8 | ** 9 | ** Licensed under the Apache License, Version 2.0 (the "License"); 10 | ** you may not use this file except in compliance with the License. 11 | ** You may obtain a copy of the License at 12 | ** 13 | ** http://www.apache.org/licenses/LICENSE-2.0 14 | ** 15 | ** Unless required by applicable law or agreed to in writing, software 16 | ** distributed under the License is distributed on an "AS IS" BASIS, 17 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | ** See the License for the specific language governing permissions and 19 | ** limitations under the License. 20 | ** 21 | ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 22 | 23 | #ifndef _CPROVIDER_H 24 | #define _CPROVIDER_H 25 | 26 | #include "helpers.h" 27 | #include "CCredential.h" 28 | #include 29 | #include 30 | #include 31 | 32 | constexpr auto MAX_CREDENTIALS = 3; 33 | constexpr auto MAX_DWORD = 0xffffffff; // maximum DWORD; 34 | 35 | enum class SERIALIZATION_AVAILABLE 36 | { 37 | // There are macros with the names 'DOMAIN' etc, therefore use prefix FOR 38 | FOR_USERNAME, 39 | FOR_PASSWORD, 40 | FOR_DOMAIN 41 | }; 42 | 43 | class CProvider : public ICredentialProvider 44 | { 45 | public: 46 | // IUnknown 47 | IFACEMETHODIMP_(ULONG) AddRef() noexcept override 48 | { 49 | return ++_cRef; 50 | } 51 | 52 | IFACEMETHODIMP_(ULONG) Release() noexcept override 53 | { 54 | LONG cRef = --_cRef; 55 | if (!cRef) 56 | { 57 | delete this; 58 | } 59 | return cRef; 60 | } 61 | 62 | #pragma warning( disable : 4838 ) 63 | IFACEMETHODIMP QueryInterface(__in REFIID riid, __deref_out void** ppv) noexcept override 64 | { 65 | static const QITAB qit[] = 66 | { 67 | QITABENT(CProvider, ICredentialProvider), // IID_ICredentialProvider 68 | { 0 }, 69 | }; 70 | return QISearch(this, qit, riid, ppv); 71 | } 72 | 73 | public: 74 | IFACEMETHODIMP SetUsageScenario(__in CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, __in DWORD dwFlags) override; 75 | IFACEMETHODIMP SetSerialization(__in const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs) override; 76 | 77 | IFACEMETHODIMP Advise(__in ICredentialProviderEvents* pcpe, __in UINT_PTR upAdviseContext) override; 78 | IFACEMETHODIMP UnAdvise() override; 79 | 80 | IFACEMETHODIMP GetFieldDescriptorCount(__out DWORD* pdwCount) override; 81 | IFACEMETHODIMP GetFieldDescriptorAt(__in DWORD dwIndex, __deref_out CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR** ppcpfd) override; 82 | 83 | IFACEMETHODIMP GetCredentialCount(__out DWORD* pdwCount, __out_range(<, *pdwCount) DWORD* pdwDefault, 84 | __out BOOL* pbAutoLogonWithDefault) override; 85 | 86 | IFACEMETHODIMP GetCredentialAt(__in DWORD dwIndex, __deref_out ICredentialProviderCredential** ppcpc) override; 87 | 88 | friend HRESULT CSample_CreateInstance(__in REFIID riid, __deref_out void** ppv); 89 | 90 | protected: 91 | CProvider(); 92 | __override ~CProvider(); 93 | 94 | private: 95 | void _CleanupSetSerialization(); 96 | 97 | void _GetSerializedCredentials(PWSTR *username, PWSTR *password, PWSTR *domain); 98 | 99 | bool _SerializationAvailable(SERIALIZATION_AVAILABLE checkFor); 100 | 101 | private: 102 | LONG _cRef; 103 | 104 | KERB_INTERACTIVE_UNLOCK_LOGON* _pkiulSetSerialization; 105 | 106 | std::unique_ptr _credential; 107 | 108 | std::shared_ptr _config; 109 | }; 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /doc/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Installation 4 | ============ 5 | 6 | Prerequisites 7 | ------------- 8 | 9 | To use the privacyIDEA Credential Provider, you need to have a privacyIDEA 10 | Authentication System. The installation and setup of this backend is covered 11 | in another documentation [#privacyideaSetup]_. 12 | 13 | Ask the company NetKnights to get an evaluation version of the privacyIDEA 14 | Credential Provider [#contact]_. 15 | 16 | MSI package 17 | ----------- 18 | 19 | The privacyIDEA Credential Provider comes as 64bit MSI package. 20 | You can install it manually or use your software distribution tool. 21 | In the installer, you can configure the most important options, but some less commonly used 22 | ones are not in the installer. You can find all options and explanations here `ref:configuration` 23 | 24 | 25 | Automated Deployment 26 | -------------------- 27 | 28 | If you want to deploy the privacyIDEA Credential Provider to a lot of targets with automation, here are some options on how 29 | to configure it automatically: 30 | 1. You can set the properties of the installer via command line: ``c:\> msiexec /i yourmsi.msi THEPROPERTYNAME=valueofproperty``. 31 | The properties are usually the registry entry name in all caps, so two_step_send_password would be TWO_STEP_SEND_PASSWORD. 32 | A list of properties can be found `Link here https://github.com/privacyidea/privacyidea-credential-provider/blob/196779ffe1df5e10cc7c79d9425a311b0172be97/WiXSetup/Product.wxs#L284`_ 33 | 34 | 2. Use the `Link Orca/SuperOrca Tool https://www.heise.de/download/product/superorca-56658`_ to prepare a MSI with the properties set: 35 | .. image:: /install_images/orca.png 36 | 37 | 3. Automate the manual installation described in the next paragraph. 38 | 39 | Manual Installation 40 | ------------------- 41 | 42 | The privacyIDEA Credential Provider and Filter can also be registered manually. 43 | To do this, the file ``PrivacyIDEACredentialProvider.dll`` has to be put into ``%windir%\System32``. 44 | (If desired, the ``PrivacyIDEACredentialProviderFilter.dll`` can be added aswell). 45 | Next, the privacyIDEA Credential Provider has to be registered to be loaded into the logon process. 46 | This is done by adding its CLSID to the list of Credential Providers at 47 | ``HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\``. 48 | Add a new key here with the name ``{7BAF541E-F8E0-4EDF-B69A-BD2771139E8E}`` (the CLSID). 49 | Afterwards set the data of the default to ``PrivacyIDEACredentialProvider``. 50 | Finally, the DLL has to be registered with the system. To do this, go to ``HKEY_CLASSES_ROOT\CLSID\`` 51 | and add a new key with the CLSID from above. Add another key to the on just created with the name 52 | ``InprocServer32``. Set the default data to ``PrivacyIDEACredentialProvider.dll`` and add another 53 | REG_SZ with the name ``ThreadingModel`` and data ``Apartment``. 54 | Now the privacyIDEA Credential Provider is registered and should be visible at the next Login attempt. 55 | This can also be done via the file ``RegisterProvider.reg``. 56 | 57 | If you wish to also use the privacyIDEA Credential Provider Filter, do the steps above again with the 58 | CLSID of the Filter which is ``{34065473-D75F-4BC2-9782-E98E63ED0D41}`` and registration at 59 | ``HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Provider Filters\``. 60 | Alternativly, the file ``RegisterFilter.reg`` can be used. 61 | 62 | To unregister, the corresponding files ``UnregisterXXX.reg`` can be used. 63 | This does not remove the configuration, DLL files or CLSID entries, it only removes the Provider or Filter 64 | from the Authentication flow at ````HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\``. 65 | 66 | 67 | .. [#privacyideaSetup] http://privacyidea.readthedocs.io/en/latest/installation/index.html 68 | .. [#contact] https://netknights.it/en/company/contact/ 69 | 70 | -------------------------------------------------------------------------------- /CredentialProvider/helpers.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 2017 NetKnights GmbH 5 | ** 6 | ** Author Dominik Pretzsch 7 | ** Nils Behlen 8 | ** 9 | ** Licensed under the Apache License, Version 2.0 (the "License"); 10 | ** you may not use this file except in compliance with the License. 11 | ** You may obtain a copy of the License at 12 | ** 13 | ** http://www.apache.org/licenses/LICENSE-2.0 14 | ** 15 | ** Unless required by applicable law or agreed to in writing, software 16 | ** distributed under the License is distributed on an "AS IS" BASIS, 17 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | ** See the License for the specific language governing permissions and 19 | ** limitations under the License. 20 | ** 21 | ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 22 | 23 | #pragma once 24 | #include 25 | #include 26 | #define SECURITY_WIN32 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #pragma warning(push) 33 | #pragma warning(disable : 4995) 34 | #include 35 | #pragma warning(pop) 36 | 37 | //makes a copy of a field descriptor using CoTaskMemAlloc 38 | HRESULT FieldDescriptorCoAllocCopy( 39 | __in const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR& rcpfd, 40 | __deref_out CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR** ppcpfd 41 | ); 42 | 43 | //makes a copy of a field descriptor on the normal heap 44 | HRESULT FieldDescriptorCopy( 45 | __in const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR& rcpfd, 46 | __deref_out CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* pcpfd 47 | ); 48 | 49 | //creates a UNICODE_STRING from a NULL-terminated string 50 | HRESULT UnicodeStringInitWithString( 51 | __in PWSTR pwz, 52 | __out UNICODE_STRING* pus 53 | ); 54 | 55 | //initializes a KERB_INTERACTIVE_UNLOCK_LOGON with weak references to the provided credentials 56 | HRESULT KerbInteractiveUnlockLogonInit( 57 | __in PWSTR pwzDomain, 58 | __in PWSTR pwzUsername, 59 | __in PWSTR pwzPassword, 60 | __in CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, 61 | __out KERB_INTERACTIVE_UNLOCK_LOGON* pkiul 62 | ); 63 | 64 | //packages the credentials into the buffer that the system expects 65 | HRESULT KerbInteractiveUnlockLogonPack( 66 | __in const KERB_INTERACTIVE_UNLOCK_LOGON& rkiulIn, 67 | __deref_out_bcount(*pcb) BYTE** prgb, 68 | __out DWORD* pcb 69 | ); 70 | 71 | //packages the credentials into the buffer that the system expects 72 | HRESULT KerbChangePasswordPack( 73 | __in const KERB_CHANGEPASSWORD_REQUEST& rkcrIn, 74 | __deref_out_bcount(*pcb) BYTE** prgb, 75 | __out DWORD* pcb 76 | ); 77 | 78 | //get the authentication package that will be used for our logon attempt 79 | HRESULT RetrieveNegotiateAuthPackage( 80 | __out ULONG* pulAuthPackage 81 | ); 82 | 83 | //encrypt a password (if necessary) and copy it; if not, just copy it 84 | HRESULT ProtectIfNecessaryAndCopyPassword( 85 | __in PCWSTR pwzPassword, 86 | __in CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, 87 | __deref_out PWSTR* ppwzProtectedPassword 88 | ); 89 | 90 | HRESULT UnProtectIfNecessaryAndCopyPassword( 91 | __in PCWSTR pwzPassword, 92 | __deref_out PWSTR* ppwzUnProtectedPassword 93 | ); 94 | 95 | HRESULT _UnProtectAndCopyString( 96 | __in PCWSTR pwzToUnProtect, 97 | __deref_out PWSTR* ppwzUnProtected 98 | ); 99 | 100 | HRESULT KerbInteractiveUnlockLogonRepackNative( 101 | __in_bcount(cbWow) BYTE* rgbWow, 102 | __in DWORD cbWow, 103 | __deref_out_bcount(*pcbNative) BYTE** prgbNative, 104 | __out DWORD* pcbNative 105 | ); 106 | 107 | void KerbInteractiveUnlockLogonUnpackInPlace( 108 | __inout_bcount(cb) KERB_INTERACTIVE_UNLOCK_LOGON* pkiul, 109 | __in DWORD cb 110 | ); 111 | 112 | HRESULT DomainUsernameStringAlloc( 113 | __in PCWSTR pwszDomain, 114 | __in PCWSTR pwszUsername, 115 | __deref_out PWSTR* ppwszDomainUsername 116 | ); 117 | -------------------------------------------------------------------------------- /CredentialProvider/Dll.cpp: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 2017 NetKnights GmbH 5 | ** 6 | ** Author Dominik Pretzsch 7 | ** Nils Behlen 8 | ** 9 | ** Licensed under the Apache License, Version 2.0 (the "License"); 10 | ** you may not use this file except in compliance with the License. 11 | ** You may obtain a copy of the License at 12 | ** 13 | ** http://www.apache.org/licenses/LICENSE-2.0 14 | ** 15 | ** Unless required by applicable law or agreed to in writing, software 16 | ** distributed under the License is distributed on an "AS IS" BASIS, 17 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | ** See the License for the specific language governing permissions and 19 | ** limitations under the License. 20 | ** 21 | ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 22 | 23 | #include "Dll.h" 24 | 25 | static LONG g_cRef = 0; // global dll reference count 26 | HINSTANCE g_hinst = nullptr; // global dll hinstance 27 | 28 | extern HRESULT CSample_CreateInstance(__in REFIID riid, __deref_out void** ppv); 29 | EXTERN_C GUID CLSID_CSample; 30 | 31 | class CClassFactory : public IClassFactory 32 | { 33 | public: 34 | CClassFactory() : _cRef(1) 35 | {} 36 | 37 | // IUnknown 38 | #pragma warning( disable : 4838 ) 39 | IFACEMETHODIMP QueryInterface(__in REFIID riid, __deref_out void** ppv) noexcept 40 | { 41 | static const QITAB qit[] = 42 | { 43 | QITABENT(CClassFactory, IClassFactory), 44 | { 0 }, 45 | }; 46 | return QISearch(this, qit, riid, ppv); 47 | } 48 | 49 | IFACEMETHODIMP_(ULONG) AddRef() noexcept 50 | { 51 | return InterlockedIncrement(&_cRef); 52 | } 53 | 54 | IFACEMETHODIMP_(ULONG) Release() noexcept 55 | { 56 | LONG const cRef = InterlockedDecrement(&_cRef); 57 | if (!cRef) 58 | delete this; 59 | return cRef; 60 | } 61 | 62 | // IClassFactory 63 | IFACEMETHODIMP CreateInstance(__in IUnknown* pUnkOuter, __in REFIID riid, __deref_out void** ppv) 64 | { 65 | HRESULT hr; 66 | if (!pUnkOuter) 67 | { 68 | hr = CSample_CreateInstance(riid, ppv); 69 | } 70 | else 71 | { 72 | *ppv = nullptr; 73 | hr = CLASS_E_NOAGGREGATION; 74 | } 75 | return hr; 76 | } 77 | 78 | IFACEMETHODIMP LockServer(__in BOOL bLock) 79 | { 80 | if (bLock) 81 | { 82 | DllAddRef(); 83 | } 84 | else 85 | { 86 | DllRelease(); 87 | } 88 | return S_OK; 89 | } 90 | 91 | private: 92 | ~CClassFactory() 93 | {} 94 | long _cRef; 95 | }; 96 | 97 | HRESULT CClassFactory_CreateInstance(__in REFCLSID rclsid, __in REFIID riid, __deref_out void** ppv) 98 | { 99 | *ppv = nullptr; 100 | 101 | HRESULT hr; 102 | 103 | if (CLSID_CSample == rclsid) 104 | { 105 | CClassFactory* pcf = new CClassFactory(); 106 | if (pcf) 107 | { 108 | hr = pcf->QueryInterface(riid, ppv); 109 | pcf->Release(); 110 | } 111 | else 112 | { 113 | hr = E_OUTOFMEMORY; 114 | } 115 | } 116 | else 117 | { 118 | hr = CLASS_E_CLASSNOTAVAILABLE; 119 | } 120 | return hr; 121 | } 122 | 123 | void DllAddRef() noexcept 124 | { 125 | InterlockedIncrement(&g_cRef); 126 | } 127 | 128 | void DllRelease() noexcept 129 | { 130 | InterlockedDecrement(&g_cRef); 131 | } 132 | 133 | STDAPI DllCanUnloadNow() 134 | { 135 | return (g_cRef > 0) ? S_FALSE : S_OK; 136 | } 137 | 138 | STDAPI DllGetClassObject(__in REFCLSID rclsid, __in REFIID riid, __deref_out void** ppv) 139 | { 140 | return CClassFactory_CreateInstance(rclsid, riid, ppv); 141 | } 142 | 143 | STDAPI_(BOOL) DllMain(__in HINSTANCE hinstDll, __in DWORD dwReason, __in void*) 144 | { 145 | switch (dwReason) 146 | { 147 | case DLL_PROCESS_ATTACH: 148 | DisableThreadLibraryCalls(hinstDll); 149 | break; 150 | case DLL_PROCESS_DETACH: 151 | case DLL_THREAD_ATTACH: 152 | case DLL_THREAD_DETACH: 153 | default: 154 | break; 155 | } 156 | 157 | g_hinst = hinstDll; 158 | return TRUE; 159 | } 160 | 161 | -------------------------------------------------------------------------------- /CppClient/CppClient/PIResponse.cpp: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #include "PIResponse.h" 21 | 22 | bool PIResponse::IsPushAvailable() 23 | { 24 | for (auto& challenge : challenges) 25 | { 26 | if (challenge.type == "push" || challenge.type == "smartphone") 27 | { 28 | return true; 29 | } 30 | } 31 | return false; 32 | } 33 | 34 | bool PIResponse::isAuthenticationSuccessful() const 35 | { 36 | if (authenticationStatus == AuthenticationStatus::ACCEPT) 37 | { 38 | return true; 39 | } 40 | else if (authenticationStatus == AuthenticationStatus::NOT_SET && challenges.empty() && value) 41 | { 42 | return true; 43 | } 44 | return false; 45 | } 46 | 47 | std::string PIResponse::GetPushMessage() 48 | { 49 | for (auto& challenge : challenges) 50 | { 51 | if (challenge.type == "push") 52 | { 53 | return challenge.message; 54 | } 55 | } 56 | return ""; 57 | } 58 | 59 | std::optional PIResponse::GetFIDOSignRequest() 60 | { 61 | std::optional ret = std::nullopt; 62 | std::vector allowCredentials; 63 | FIDOSignRequest signRequest; 64 | for (auto& challenge : challenges) 65 | { 66 | if (challenge.type == "webauthn" || challenge.type == "passkey") 67 | { 68 | if (challenge.type == "webauthn" && challenge.fidoSignRequest 69 | && !challenge.fidoSignRequest.value().allowCredentials.empty()) 70 | { 71 | allowCredentials.push_back(challenge.fidoSignRequest.value().allowCredentials.at(0)); 72 | } 73 | // Set the RP ID only once 74 | if (signRequest.rpId.empty()) 75 | { 76 | signRequest = challenge.fidoSignRequest.value(); 77 | } 78 | } 79 | } 80 | 81 | if (!signRequest.challenge.empty() && !signRequest.rpId.empty()) 82 | { 83 | signRequest.allowCredentials = allowCredentials; 84 | ret = signRequest; 85 | } 86 | 87 | return ret; 88 | } 89 | 90 | std::string Concatenate(std::vector vec) 91 | { 92 | std::string msg; 93 | for (auto& m : vec) 94 | { 95 | msg += m + ", "; 96 | } 97 | // Remove the last comma and space 98 | if (!msg.empty()) 99 | { 100 | msg.pop_back(); 101 | msg.pop_back(); 102 | } 103 | 104 | return msg; 105 | } 106 | 107 | std::string PIResponse::GetFIDOMessage() 108 | { 109 | if (challenges.empty()) 110 | { 111 | return ""; 112 | } 113 | std::vector messages; 114 | for (auto& challenge : challenges) 115 | { 116 | if (std::find(messages.begin(), messages.end(), challenge.message) == messages.end() 117 | && (challenge.type == "webauthn" || challenge.type == "passkey")) 118 | { 119 | messages.push_back(challenge.message); 120 | } 121 | } 122 | return Concatenate(messages); 123 | } 124 | 125 | std::string PIResponse::GetNonFIDOMessage() 126 | { 127 | if (challenges.empty()) 128 | { 129 | return ""; 130 | } 131 | std::vector messages; 132 | for (auto& challenge : challenges) 133 | { 134 | if (std::find(messages.begin(), messages.end(), challenge.message) == messages.end() 135 | && challenge.type != "webauthn" && challenge.type != "passkey") 136 | { 137 | messages.push_back(challenge.message); 138 | } 139 | } 140 | return Concatenate(messages); 141 | } 142 | 143 | bool PIResponse::IsVersionHigherOrEqual(int major, int minor, int patch) const 144 | { 145 | if (privacyIDEAVersionMajor > major) 146 | { 147 | return true; 148 | } 149 | else if (privacyIDEAVersionMajor == major) 150 | { 151 | if (privacyIDEAVersionMinor > minor) 152 | { 153 | return true; 154 | } 155 | else if (privacyIDEAVersionMinor == minor) 156 | { 157 | return privacyIDEAVersionPatch >= patch; 158 | } 159 | } 160 | return false; 161 | } 162 | -------------------------------------------------------------------------------- /Shared/Shared.cpp: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2020 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #include "Shared.h" 21 | #include "Logger.h" 22 | #include "RegistryReader.h" 23 | #include "Convert.h" 24 | #include 25 | 26 | namespace Shared 27 | { 28 | bool IsRequiredForScenario(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, int caller) 29 | { 30 | PIDebug(__FUNCTION__); 31 | if (caller != FILTER && caller != PROVIDER) 32 | { 33 | PIDebug("Invalid argument for caller: " + std::to_string(caller)); 34 | return false; 35 | } 36 | 37 | RegistryReader rr(L"SOFTWARE\\Netknights GmbH\\PrivacyIDEA-CP\\"); 38 | std::wstring entry; 39 | const bool isRemote = Shared::IsCurrentSessionRemote(); 40 | switch (cpus) 41 | { 42 | case CPUS_LOGON: 43 | { 44 | entry = rr.GetWString(L"cpus_logon"); 45 | break; 46 | } 47 | case CPUS_UNLOCK_WORKSTATION: 48 | { 49 | entry = rr.GetWString(L"cpus_unlock"); 50 | break; 51 | } 52 | case CPUS_CREDUI: 53 | { 54 | entry = rr.GetWString(L"cpus_credui"); 55 | break; 56 | } 57 | case CPUS_CHANGE_PASSWORD: 58 | case CPUS_PLAP: 59 | case CPUS_INVALID: 60 | return false; 61 | default: 62 | return false; 63 | } 64 | std::string strCaller = (caller == 0 ? "Provider" : "Filter"); 65 | PIDebug("Checking for " + strCaller + ", " + CPUStoString(cpus) + ", " + (isRemote ? "remote" : "local") 66 | + ", entry=" + Convert::ToString(entry)); 67 | // default - no additional config found 68 | if (entry.empty()) return true; 69 | 70 | if (caller == FILTER) 71 | { 72 | // Check that we don't filter if the CP is not enumerated 73 | return (entry == L"0e" || (entry == L"1e" && isRemote) || (entry == L"2e" && !isRemote)); 74 | } 75 | else if (caller == PROVIDER) 76 | { 77 | // 0 means fully enabled, 1-only remote, 2-non-remote, 3-disabled 78 | return ((entry.at(0) == L'1' && isRemote) || (entry.at(0) == L'2' && !isRemote) || (entry.at(0) == L'0')); 79 | } 80 | 81 | return false; 82 | } 83 | 84 | #define TERMINAL_SERVER_KEY _T("SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\") 85 | #define GLASS_SESSION_ID _T("GlassSessionId") 86 | bool IsCurrentSessionRemote() 87 | { 88 | bool fIsRemoteable = false; 89 | if (GetSystemMetrics(SM_REMOTESESSION)) 90 | { 91 | fIsRemoteable = true; 92 | } 93 | else 94 | { 95 | HKEY hRegKey = nullptr; 96 | LONG lResult; 97 | 98 | lResult = RegOpenKeyEx( 99 | HKEY_LOCAL_MACHINE, 100 | TERMINAL_SERVER_KEY, 101 | 0, // ulOptions 102 | KEY_READ, 103 | &hRegKey 104 | ); 105 | 106 | if (lResult == ERROR_SUCCESS) 107 | { 108 | DWORD dwGlassSessionId = 0; 109 | DWORD cbGlassSessionId = sizeof(dwGlassSessionId); 110 | DWORD dwType = 0; 111 | 112 | lResult = RegQueryValueEx( 113 | hRegKey, 114 | GLASS_SESSION_ID, 115 | NULL, // lpReserved 116 | &dwType, 117 | (BYTE*)&dwGlassSessionId, 118 | &cbGlassSessionId 119 | ); 120 | 121 | if (lResult == ERROR_SUCCESS) 122 | { 123 | DWORD dwCurrentSessionId; 124 | 125 | if (ProcessIdToSessionId(GetCurrentProcessId(), &dwCurrentSessionId)) 126 | { 127 | fIsRemoteable = (dwCurrentSessionId != dwGlassSessionId); 128 | } 129 | } 130 | } 131 | 132 | if (hRegKey) 133 | { 134 | RegCloseKey(hRegKey); 135 | } 136 | } 137 | 138 | PIDebug(fIsRemoteable ? "Session is remote" : "Session is local"); 139 | 140 | return fIsRemoteable; 141 | } 142 | 143 | std::string CPUStoString(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus) 144 | { 145 | switch (cpus) 146 | { 147 | case CPUS_LOGON: 148 | return "CPUS_LOGON"; 149 | case CPUS_UNLOCK_WORKSTATION: 150 | return "CPUS_UNLOCK_WORKSTATION"; 151 | case CPUS_CREDUI: 152 | return "CPUS_CREDUI"; 153 | case CPUS_CHANGE_PASSWORD: 154 | return "CPUS_CHANGE_PASSWORD"; 155 | case CPUS_PLAP: 156 | return "CPUS_PLAP"; 157 | case CPUS_INVALID: 158 | return "CPUS_INVALID"; 159 | default: 160 | return ("Unknown CPUS: " + std::to_string(cpus)); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /CredentialProvider/Translator.cpp: -------------------------------------------------------------------------------- 1 | #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 2 | 3 | #include "Translator.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | using json = nlohmann::json; 14 | 15 | RegistryReader rr(CONFIG_REGISTRY_PATH); // Gets registry keys 16 | std::wstring Translator::_localesPath = rr.GetWString(L"localesPath"); // Get locales path 17 | 18 | std::unordered_map Translator::_translations; 19 | std::string Translator::_currentLanguage; 20 | std::string Translator::_currentRegion; 21 | 22 | Translator::Translator() 23 | { 24 | _currentLanguage = GetUserLocale(); 25 | SetLanguage(_currentLanguage); 26 | }; 27 | 28 | void Translator::SetLanguage(const std::string& language) 29 | { 30 | std::string languageOnly = GetLanguageFromLocale(language); 31 | std::string region = GetRegionFromLocale(language); 32 | PIDebug("Translation language " + languageOnly + ", region " + region); 33 | if (TryLoadTranslations(languageOnly, region)) 34 | { 35 | _currentLanguage = languageOnly; 36 | _currentRegion = region; 37 | } 38 | else if (TryLoadTranslations(languageOnly)) 39 | { 40 | _currentLanguage = languageOnly; 41 | _currentRegion.clear(); 42 | } 43 | else 44 | { 45 | // Fallback to English 46 | TryLoadTranslations("en"); 47 | _currentLanguage = "en"; 48 | _currentRegion.clear(); 49 | } 50 | } 51 | 52 | std::wstring Translator::Translate(int textId) 53 | { 54 | const auto it = _translations.find(textId); 55 | if (it != _translations.end()) 56 | { 57 | return it->second; // Return the translated wstring 58 | } 59 | // If translation not found, return "undefined" 60 | return L"undefined"; 61 | } 62 | 63 | std::string Translator::GetLanguage() 64 | { 65 | return _currentLanguage; 66 | } 67 | 68 | std::string Translator::GetRegion() 69 | { 70 | return _currentRegion; 71 | } 72 | 73 | std::string Translator::GetUserLocale() 74 | { 75 | wchar_t localeName[LOCALE_NAME_MAX_LENGTH] = { 0 }; 76 | if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) != 0) 77 | { 78 | char narrowLocaleName[LOCALE_NAME_MAX_LENGTH] = { 0 }; 79 | if (WideCharToMultiByte(CP_UTF8, 0, localeName, -1, narrowLocaleName, LOCALE_NAME_MAX_LENGTH, nullptr, nullptr) > 0) 80 | { 81 | return narrowLocaleName; 82 | } 83 | } 84 | // Return fallback language "en" if failed to detect user locale 85 | return "en"; 86 | } 87 | 88 | bool Translator::TryLoadTranslations(const std::string& language, const std::string& region) 89 | { 90 | std::string locale = language; 91 | if (!region.empty()) 92 | { 93 | locale += "_" + region; 94 | } 95 | return LoadTranslations(locale); 96 | } 97 | 98 | bool Translator::LoadTranslations(const std::string& locale) 99 | { 100 | std::wstring_convert> converter; 101 | std::string path = converter.to_bytes(_localesPath); 102 | 103 | std::string filePath = path + "\\" + locale + ".json"; 104 | std::ifstream file(filePath); 105 | 106 | //PIDebug("Trying to open translation from " + filePath); 107 | if (!file.is_open()) 108 | { 109 | PIDebug("Can not load translation file: " + filePath); 110 | return false; 111 | } 112 | 113 | json data{}; 114 | 115 | try 116 | { 117 | file >> data; 118 | } 119 | catch (const std::exception& e) 120 | { 121 | UNREFERENCED_PARAMETER(e); 122 | PIDebug("Error parsing translation file:" + filePath); 123 | return false; 124 | } 125 | 126 | PIDebug("Loading translation from " + filePath); 127 | 128 | _translations.clear(); // Clear existing _translations 129 | for (auto it = data.begin(); it != data.end(); ++it) 130 | { 131 | const int key = std::stoi(it.key()); 132 | std::string value = it.value(); 133 | //PIDebug("Loading translation: " + it.key() + ":" + value); 134 | _translations[key] = std::wstring_convert>().from_bytes(value); 135 | } 136 | return true; 137 | } 138 | 139 | std::string Translator::GetLanguageFromLocale(const std::string& locale) 140 | { 141 | // Regular expression to match language part of the locale string 142 | std::regex languageRegex("([a-z]{2})"); 143 | std::smatch match; 144 | if (std::regex_search(locale, match, languageRegex)) 145 | { 146 | return match[1].str(); 147 | } 148 | // Return empty string if language part not found 149 | return ""; 150 | } 151 | 152 | std::string Translator::GetRegionFromLocale(const std::string& locale) 153 | { 154 | // Regular expression to match region part of the locale string 155 | std::regex regionRegex("[a-z]{2}[_-]([A-Z]{2})"); 156 | std::smatch match; 157 | if (std::regex_search(locale, match, regionRegex)) 158 | { 159 | return match[1].str(); 160 | } 161 | // Return empty string if region part not found 162 | return ""; 163 | } 164 | -------------------------------------------------------------------------------- /CredentialProviderFilter/CCredentialProviderFilter.cpp: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 2017 NetKnights GmbH 5 | ** 6 | ** Author Dominik Pretzsch 7 | ** Nils Behlen 8 | ** 9 | ** Licensed under the Apache License, Version 2.0 (the "License"); 10 | ** you may not use this file except in compliance with the License. 11 | ** You may obtain a copy of the License at 12 | ** 13 | ** http://www.apache.org/licenses/LICENSE-2.0 14 | ** 15 | ** Unless required by applicable law or agreed to in writing, software 16 | ** distributed under the License is distributed on an "AS IS" BASIS, 17 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | ** See the License for the specific language governing permissions and 19 | ** limitations under the License. 20 | ** 21 | ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 22 | 23 | #ifndef WIN32_NO_STATUS 24 | #include 25 | #define WIN32_NO_STATUS 26 | #endif 27 | #include "CCredentialProviderFilter.h" 28 | #include "guid.h" 29 | #include "Logger.h" 30 | #include "Shared.h" 31 | #include "Convert.h" 32 | #include 33 | #include 34 | 35 | HRESULT CSample_CreateInstance(__in REFIID riid, __deref_out void** ppv) 36 | { 37 | RegistryReader rr(CONFIG_REGISTRY_PATH); 38 | Logger::Get().logDebug = rr.GetBool(L"debug_log"); 39 | 40 | PIDebug(std::string(__FUNCTION__) + " - FILTER START"); 41 | HRESULT hr = S_OK; 42 | 43 | CCredentialProviderFilter* pProvider = new CCredentialProviderFilter(); 44 | 45 | if (pProvider) 46 | { 47 | hr = pProvider->QueryInterface(riid, ppv); 48 | pProvider->Release(); 49 | } 50 | else 51 | { 52 | hr = E_OUTOFMEMORY; 53 | } 54 | 55 | return hr; 56 | } 57 | 58 | HRESULT CCredentialProviderFilter::Filter(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, DWORD dwFlags, GUID* rgclsidProviders, 59 | BOOL* rgbAllow, DWORD cProviders) 60 | { 61 | UNREFERENCED_PARAMETER(dwFlags); 62 | PIDebug(std::string(__FUNCTION__) + " " + Shared::CPUStoString(cpus)); 63 | 64 | RegistryReader rr(CONFIG_REGISTRY_PATH); 65 | _filterEnabled = rr.GetBool(L"enable_filter"); 66 | 67 | if (!_filterEnabled) 68 | { 69 | PIDebug("Filter disabled by registry setting!"); 70 | return S_OK; 71 | } 72 | 73 | switch (cpus) 74 | { 75 | case CPUS_LOGON: 76 | case CPUS_UNLOCK_WORKSTATION: 77 | case CPUS_CREDUI: 78 | break; 79 | case CPUS_CHANGE_PASSWORD: 80 | return E_NOTIMPL; 81 | default: 82 | return E_INVALIDARG; 83 | } 84 | 85 | if (!Shared::IsRequiredForScenario(cpus, FILTER)) 86 | { 87 | PIDebug("Filter is configured to be disabled for this scenario."); 88 | return S_OK; 89 | } 90 | 91 | std::vector whitelistedGUIDs; 92 | auto whitelist = rr.GetMultiSZ(L"filter_whitelist"); 93 | // If its CredUI, add the FIDO CP to the whitelist, so security keys can be selected 94 | if (cpus == CPUS_CREDUI) 95 | { 96 | whitelist.push_back(L"{F8A1793B-7873-4046-B2A7-1F318747F427}"); 97 | } 98 | if (!whitelist.empty()) 99 | { 100 | PIDebug("Entries for filter whitelist found:"); 101 | PIDebug(Convert::JoinW(whitelist, L", ")); 102 | HRESULT hr = S_OK; 103 | // Convert the wstrings to GUIDs 104 | for (auto& ws : whitelist) 105 | { 106 | CLSID clsid; 107 | hr = CLSIDFromString(ws.c_str(), &clsid); 108 | if (SUCCEEDED(hr)) 109 | { 110 | whitelistedGUIDs.push_back(clsid); 111 | PIDebug(L"Added " + ws + L" to whitelisted GUIDs"); 112 | } 113 | else 114 | { 115 | PIError(L"Failed to convert " + ws + L" to GUID. Check if the format is correct."); 116 | } 117 | } 118 | } 119 | 120 | for (DWORD i = 0; i < cProviders; i++) 121 | { 122 | rgbAllow[i] = FALSE; 123 | 124 | // Check if it is our own provider 125 | if (IsEqualGUID(rgclsidProviders[i], CLSID_COTP_LOGON)) 126 | { 127 | rgbAllow[i] = TRUE; 128 | } 129 | 130 | // Check if it a whitelisted provider 131 | for (auto& guid : whitelistedGUIDs) 132 | { 133 | if (IsEqualGUID(rgclsidProviders[i], guid)) 134 | { 135 | rgbAllow[i] = TRUE; 136 | } 137 | } 138 | } 139 | 140 | return S_OK; 141 | } 142 | 143 | CCredentialProviderFilter::CCredentialProviderFilter() : 144 | _cRef(1) 145 | { 146 | PIDebug(__FUNCTION__); 147 | DllAddRef(); 148 | } 149 | 150 | CCredentialProviderFilter::~CCredentialProviderFilter() 151 | { 152 | PIDebug(__FUNCTION__); 153 | DllRelease(); 154 | } 155 | 156 | HRESULT CCredentialProviderFilter::UpdateRemoteCredential(const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcsIn, CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcsOut) 157 | { 158 | //UNREFERENCED_PARAMETER(pcpsIn); 159 | //UNREFERENCED_PARAMETER(pcpcsOut); 160 | PIDebug(__FUNCTION__); 161 | 162 | if (!pcpcsIn) 163 | { 164 | // no point continuing as there are no credentials 165 | return E_NOTIMPL; 166 | } 167 | 168 | // copy contents from pcpcsIn to pcpcsOut 169 | pcpcsOut->ulAuthenticationPackage = pcpcsIn->ulAuthenticationPackage; 170 | pcpcsOut->cbSerialization = pcpcsIn->cbSerialization; 171 | pcpcsOut->rgbSerialization = pcpcsIn->rgbSerialization; 172 | 173 | // set target CP to our CP 174 | pcpcsOut->clsidCredentialProvider = CLSID_COTP_LOGON; 175 | 176 | // copy the buffer contents if needed 177 | if (pcpcsOut->cbSerialization > 0 && (pcpcsOut->rgbSerialization = (BYTE*)CoTaskMemAlloc(pcpcsIn->cbSerialization)) != NULL) 178 | { 179 | CopyMemory(pcpcsOut->rgbSerialization, pcpcsIn->rgbSerialization, pcpcsIn->cbSerialization); 180 | return S_OK; 181 | } 182 | else 183 | { 184 | return E_NOTIMPL; 185 | } 186 | } -------------------------------------------------------------------------------- /CredentialProvider/RCa01848: -------------------------------------------------------------------------------- 1 | #line 1"C:\\Users\\Nils\\Desktop\\privacyidea-credentialprovider-git2\\CredentialProvider\\CredentialProvider.rc" 2 | #line 1 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource.h" 6 | #line 5 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "afxres.h" 13 | #include "version.h" 14 | #line 13 15 | ///////////////////////////////////////////////////////////////////////////// 16 | #undef APSTUDIO_READONLY_SYMBOLS 17 | #line 16 18 | ///////////////////////////////////////////////////////////////////////////// 19 | // Deutsch (Deutschland) resources 20 | #line 19 21 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) 22 | LANGUAGE LANG_GERMAN, SUBLANG_GERMAN 23 | #line 22 24 | #ifdef APSTUDIO_INVOKED 25 | ///////////////////////////////////////////////////////////////////////////// 26 | // 27 | // TEXTINCLUDE 28 | // 29 | #line 28 30 | 1 TEXTINCLUDE 31 | BEGIN 32 | "resource.h\0" 33 | END 34 | #line 33 35 | 2 TEXTINCLUDE 36 | BEGIN 37 | "#include ""afxres.h""\r\n" 38 | "#include ""version.h""\r\n" 39 | "\0" 40 | END 41 | #line 40 42 | 3 TEXTINCLUDE 43 | BEGIN 44 | "\r\n" 45 | "\0" 46 | END 47 | #line 46 48 | #endif // APSTUDIO_INVOKED 49 | #line 49 50 | ///////////////////////////////////////////////////////////////////////////// 51 | // 52 | // Version 53 | // 54 | #line 54 55 | VS_VERSION_INFO VERSIONINFO 56 | FILEVERSION VER_FILE_VERSION 57 | PRODUCTVERSION VER_PRODUCT_VERSION 58 | FILEFLAGSMASK 0x3fL 59 | FILEFLAGS VER_FILEFLAGS 60 | FILEOS VER_FILEOS 61 | FILETYPE VER_FILETYPE 62 | FILESUBTYPE 0x0L 63 | BEGIN 64 | BLOCK "StringFileInfo" 65 | BEGIN 66 | BLOCK "040904b0" 67 | BEGIN 68 | VALUE "FileDescription", VER_FILE_DESCRIPTION_STR "\0" 69 | VALUE "FileVersion", VER_FILE_VERSION_STR "\0" 70 | VALUE "InternalName", VER_INTERNAL_NAME_STR "\0" 71 | VALUE "LegalCopyright", VER_COPYRIGHT_STR "\0" 72 | VALUE "OriginalFilename", VER_ORIGINAL_FILENAME_STR "\0" 73 | VALUE "ProductName", VER_PRODUCTNAME_STR 74 | VALUE "ProductVersion", VER_PRODUCT_VERSION_STR "\0" 75 | END 76 | END 77 | BLOCK "VarFileInfo" 78 | BEGIN 79 | VALUE "Translation", 0x409, 1200 80 | END 81 | END 82 | #line 83 83 | ///////////////////////////////////////////////////////////////////////////// 84 | // 85 | // Bitmap 86 | // 87 | #line 88 88 | IDB_TILE_IMAGE BITMAP "tileimage.bmp" 89 | #endif // Deutsch (Deutschland) resources 90 | ///////////////////////////////////////////////////////////////////////////// 91 | #line 94 92 | #ifndef APSTUDIO_INVOKED 93 | ///////////////////////////////////////////////////////////////////////////// 94 | // 95 | // Generated from the TEXTINCLUDE 3 resource. 96 | // 97 | #line 101 98 | ///////////////////////////////////////////////////////////////////////////// 99 | #endif // not APSTUDIO_INVOKED 100 | -------------------------------------------------------------------------------- /CredentialProvider/RCb01848: -------------------------------------------------------------------------------- 1 | #line 1"C:\\Users\\Nils\\Desktop\\privacyidea-credentialprovider-git2\\CredentialProvider\\CredentialProvider.rc" 2 | #line 1 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource.h" 6 | #line 5 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "afxres.h" 13 | #include "version.h" 14 | #line 13 15 | ///////////////////////////////////////////////////////////////////////////// 16 | #undef APSTUDIO_READONLY_SYMBOLS 17 | #line 16 18 | ///////////////////////////////////////////////////////////////////////////// 19 | // Deutsch (Deutschland) resources 20 | #line 19 21 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) 22 | LANGUAGE LANG_GERMAN, SUBLANG_GERMAN 23 | #line 22 24 | #ifdef APSTUDIO_INVOKED 25 | ///////////////////////////////////////////////////////////////////////////// 26 | // 27 | // TEXTINCLUDE 28 | // 29 | #line 28 30 | 1 TEXTINCLUDE 31 | BEGIN 32 | "resource.h\0" 33 | END 34 | #line 33 35 | 2 TEXTINCLUDE 36 | BEGIN 37 | "#include ""afxres.h""\r\n" 38 | "#include ""version.h""\r\n" 39 | "\0" 40 | END 41 | #line 40 42 | 3 TEXTINCLUDE 43 | BEGIN 44 | "\r\n" 45 | "\0" 46 | END 47 | #line 46 48 | #endif // APSTUDIO_INVOKED 49 | #line 49 50 | ///////////////////////////////////////////////////////////////////////////// 51 | // 52 | // Version 53 | // 54 | #line 54 55 | VS_VERSION_INFO VERSIONINFO 56 | FILEVERSION VER_FILE_VERSION 57 | PRODUCTVERSION VER_PRODUCT_VERSION 58 | FILEFLAGSMASK 0x3fL 59 | FILEFLAGS VER_FILEFLAGS 60 | FILEOS VER_FILEOS 61 | FILETYPE VER_FILETYPE 62 | FILESUBTYPE 0x0L 63 | BEGIN 64 | BLOCK "StringFileInfo" 65 | BEGIN 66 | BLOCK "040904b0" 67 | BEGIN 68 | VALUE "FileDescription", VER_FILE_DESCRIPTION_STR "\0" 69 | VALUE "FileVersion", VER_FILE_VERSION_STR "\0" 70 | VALUE "InternalName", VER_INTERNAL_NAME_STR "\0" 71 | VALUE "LegalCopyright", VER_COPYRIGHT_STR "\0" 72 | VALUE "OriginalFilename", VER_ORIGINAL_FILENAME_STR "\0" 73 | VALUE "ProductName", VER_PRODUCTNAME_STR 74 | VALUE "ProductVersion", VER_PRODUCT_VERSION_STR "\0" 75 | END 76 | END 77 | BLOCK "VarFileInfo" 78 | BEGIN 79 | VALUE "Translation", 0x409, 1200 80 | END 81 | END 82 | #line 83 83 | ///////////////////////////////////////////////////////////////////////////// 84 | // 85 | // Bitmap 86 | // 87 | #line 88 88 | IDB_TILE_IMAGE BITMAP "tileimage.bmp" 89 | #endif // Deutsch (Deutschland) resources 90 | ///////////////////////////////////////////////////////////////////////////// 91 | #line 94 92 | #ifndef APSTUDIO_INVOKED 93 | ///////////////////////////////////////////////////////////////////////////// 94 | // 95 | // Generated from the TEXTINCLUDE 3 resource. 96 | // 97 | #line 101 98 | ///////////////////////////////////////////////////////////////////////////// 99 | #endif // not APSTUDIO_INVOKED 100 | -------------------------------------------------------------------------------- /CredentialProvider/RCc01848: -------------------------------------------------------------------------------- 1 | #line 1"C:\\Users\\Nils\\Desktop\\privacyidea-credentialprovider-git2\\CredentialProvider\\CredentialProvider.rc" 2 | #line 1 3 | // Microsoft Visual C++ generated resource script. 4 | // 5 | #include "resource.h" 6 | #line 5 7 | #define APSTUDIO_READONLY_SYMBOLS 8 | ///////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Generated from the TEXTINCLUDE 2 resource. 11 | // 12 | #include "afxres.h" 13 | #include "version.h" 14 | #line 13 15 | ///////////////////////////////////////////////////////////////////////////// 16 | #undef APSTUDIO_READONLY_SYMBOLS 17 | #line 16 18 | ///////////////////////////////////////////////////////////////////////////// 19 | // Deutsch (Deutschland) resources 20 | #line 19 21 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) 22 | LANGUAGE LANG_GERMAN, SUBLANG_GERMAN 23 | #line 22 24 | #ifdef APSTUDIO_INVOKED 25 | ///////////////////////////////////////////////////////////////////////////// 26 | // 27 | // TEXTINCLUDE 28 | // 29 | #line 28 30 | 1 TEXTINCLUDE 31 | BEGIN 32 | "resource.h\0" 33 | END 34 | #line 33 35 | 2 TEXTINCLUDE 36 | BEGIN 37 | "#include ""afxres.h""\r\n" 38 | "#include ""version.h""\r\n" 39 | "\0" 40 | END 41 | #line 40 42 | 3 TEXTINCLUDE 43 | BEGIN 44 | "\r\n" 45 | "\0" 46 | END 47 | #line 46 48 | #endif // APSTUDIO_INVOKED 49 | #line 49 50 | ///////////////////////////////////////////////////////////////////////////// 51 | // 52 | // Version 53 | // 54 | #line 54 55 | VS_VERSION_INFO VERSIONINFO 56 | FILEVERSION VER_FILE_VERSION 57 | PRODUCTVERSION VER_PRODUCT_VERSION 58 | FILEFLAGSMASK 0x3fL 59 | FILEFLAGS VER_FILEFLAGS 60 | FILEOS VER_FILEOS 61 | FILETYPE VER_FILETYPE 62 | FILESUBTYPE 0x0L 63 | BEGIN 64 | BLOCK "StringFileInfo" 65 | BEGIN 66 | BLOCK "040904b0" 67 | BEGIN 68 | VALUE "FileDescription", VER_FILE_DESCRIPTION_STR "\0" 69 | VALUE "FileVersion", VER_FILE_VERSION_STR "\0" 70 | VALUE "InternalName", VER_INTERNAL_NAME_STR "\0" 71 | VALUE "LegalCopyright", VER_COPYRIGHT_STR "\0" 72 | VALUE "OriginalFilename", VER_ORIGINAL_FILENAME_STR "\0" 73 | VALUE "ProductName", VER_PRODUCTNAME_STR 74 | VALUE "ProductVersion", VER_PRODUCT_VERSION_STR "\0" 75 | END 76 | END 77 | BLOCK "VarFileInfo" 78 | BEGIN 79 | VALUE "Translation", 0x409, 1200 80 | END 81 | END 82 | #line 83 83 | ///////////////////////////////////////////////////////////////////////////// 84 | // 85 | // Bitmap 86 | // 87 | #line 88 88 | IDB_TILE_IMAGE BITMAP "tileimage.bmp" 89 | #endif // Deutsch (Deutschland) resources 90 | ///////////////////////////////////////////////////////////////////////////// 91 | #line 94 92 | #ifndef APSTUDIO_INVOKED 93 | ///////////////////////////////////////////////////////////////////////////// 94 | // 95 | // Generated from the TEXTINCLUDE 3 resource. 96 | // 97 | #line 101 98 | ///////////////////////////////////////////////////////////////////////////// 99 | #endif // not APSTUDIO_INVOKED 100 | -------------------------------------------------------------------------------- /CredentialProvider/Utilities.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | #pragma once 20 | #include "Configuration.h" 21 | #include "Logger.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | constexpr auto CLEAR_FIELDS_CRYPT = 0; 28 | constexpr auto CLEAR_FIELDS_EDIT_AND_CRYPT = 1; 29 | constexpr auto CLEAR_FIELDS_ALL = 2; 30 | constexpr auto CLEAR_FIELDS_ALL_DESTROY = 3; 31 | 32 | constexpr auto MAX_SIZE_DOMAIN = 64; 33 | constexpr auto MAX_SIZE_USERNAME = 512; 34 | 35 | // Text IDs 36 | constexpr auto TEXT_USERNAME = 0; 37 | constexpr auto TEXT_PASSWORD = 1; 38 | constexpr auto TEXT_OLD_PASSWORD = 2; 39 | constexpr auto TEXT_NEW_PASSWORD = 3; 40 | constexpr auto TEXT_CONFIRM_PASSWORD = 4; 41 | constexpr auto TEXT_DOMAIN_HINT = 5; 42 | constexpr auto TEXT_OTP_FIELD = 6; 43 | constexpr auto TEXT_WRONG_OTP = 7; 44 | constexpr auto TEXT_RESET_LINK = 8; 45 | constexpr auto TEXT_AVAILABLE_OFFLINE_TOKEN = 9; 46 | constexpr auto TEXT_OTPS_REMAINING = 10; 47 | constexpr auto TEXT_GENERIC_ERROR = 11; 48 | constexpr auto TEXT_USE_ONLINE_FIDO = 12; 49 | constexpr auto TEXT_USE_OTP = 13; 50 | constexpr auto TEXT_FIDO_PIN_HINT = 14; 51 | constexpr auto TEXT_TOUCH_SEC_KEY = 15; 52 | constexpr auto TEXT_CONNECTING = 16; 53 | constexpr auto TEXT_LOGIN_TEXT = 17; 54 | constexpr auto TEXT_OTP_PROMPT = 18; 55 | constexpr auto TEXT_FIDO_NO_CREDENTIALS = 19; 56 | constexpr auto TEXT_FIDO_WAITING_FOR_DEVICE = 20; 57 | constexpr auto TEXT_FIDO_CHECKING_OFFLINE_STATUS = 21; 58 | constexpr auto TEXT_OFFLINE_REFILL = 22; 59 | constexpr auto TEXT_FIDO_ERR_PIN_BLOCKED = 23; 60 | constexpr auto TEXT_FIDO_ERR_TX = 24; 61 | constexpr auto TEXT_FIDO_ERR_PIN_INVALID = 25; 62 | constexpr auto TEXT_USE_PASSKEY = 26; 63 | constexpr auto TEXT_ENTER_USERNAME = 27; 64 | constexpr auto TEXT_ENTER_PASSWORD = 28; 65 | constexpr auto TEXT_ENTER_USERNAME_PASSWORD = 29; 66 | constexpr auto TEXT_PASSKEY_REGISTER_TOUCH = 30; 67 | constexpr auto TEXT_SEC_KEY_ENTER_PIN_PROMPT = 31; 68 | constexpr auto TEXT_PASSKEY_REGISTRATION = 32; 69 | constexpr auto TEXT_LOGIN_WITH_USERNAME = 33; 70 | constexpr auto TEXT_FIDO_CANCELLED = 34; 71 | constexpr auto TEXT_CANCEL_ENROLLMENT = 35; 72 | constexpr auto TEXT_USE_OFFLINE_FIDO = 36; 73 | constexpr auto TEXT_FIDO_ERR_NO_CREDENTIALS = 37; 74 | 75 | class Utilities 76 | { 77 | public: 78 | Utilities(std::shared_ptr c) noexcept; 79 | 80 | /// 81 | /// If the text for the id is configurable and exists in the config, return that value. 82 | /// Otherwise, return the default text for the id in english or german, depending on GetUserDefaultUILanguage. 83 | /// 84 | /// 85 | /// 86 | std::wstring GetText(int id); 87 | 88 | HRESULT KerberosLogon( 89 | __out CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE*& pcpgsr, 90 | __out CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION*& pcpcs, 91 | __in CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, 92 | __in std::wstring username, 93 | __in std::wstring password, 94 | __in std::wstring domain 95 | ); 96 | 97 | HRESULT KerberosChangePassword( 98 | __out CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr, 99 | __out CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs, 100 | __in std::wstring username, 101 | __in std::wstring passwordOld, 102 | __in std::wstring passwordNew, 103 | __in std::wstring domain 104 | ); 105 | 106 | HRESULT CredPackAuthentication( 107 | __out CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE*& pcpgsr, 108 | __out CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION*& pcpcs, 109 | __in CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, 110 | __in std::wstring username, 111 | __in std::wstring password, 112 | __in std::wstring domain 113 | ); 114 | 115 | HRESULT Clear( 116 | wchar_t* (&field_strings)[FID_NUM_FIELDS], 117 | CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR (&pcpfd)[FID_NUM_FIELDS], 118 | ICredentialProviderCredential* pcpc, 119 | ICredentialProviderCredentialEvents* pcpce, 120 | char clear 121 | ); 122 | 123 | HRESULT SetFieldStatePairBatch( 124 | __in ICredentialProviderCredential* self, 125 | __in ICredentialProviderCredentialEvents* pCPCE, 126 | __in const FIELD_STATE_PAIR* pFSP 127 | ); 128 | 129 | HRESULT InitializeField( 130 | LPWSTR rgFieldStrings[FID_NUM_FIELDS], 131 | DWORD fieldIndex 132 | ); 133 | 134 | static std::wstring ComputerName(); 135 | 136 | /// 137 | /// Split the input into user and domain. The possible formats are: domain\user and user@domain, check in that order. 138 | /// If no '\' or '@' exsists in the input, the whole input is assumed to be the username. 139 | /// If the domain is '.', it will be resolved to the local computer name. 140 | /// 141 | /// 142 | /// 143 | /// 144 | static void SplitUserAndDomain(const std::wstring& input, std::wstring& username, std::wstring& domain); 145 | 146 | /// 147 | /// Check if the input is an UPN. The check is very basic and assumes the input is an UPN if it contains an @ and no \. 148 | /// 149 | /// 150 | /// 151 | /// bool if upn detected, false otherwise 152 | static bool CheckForUPN(const std::wstring& input) noexcept; 153 | 154 | HRESULT CopyInputFields(); 155 | 156 | HRESULT CopyUsernameField(); 157 | 158 | HRESULT CopyPasswordField(); 159 | 160 | HRESULT CopyOTPField(); 161 | 162 | HRESULT CopyWANPinField(); 163 | 164 | HRESULT CopyPasswordChangeFields(); 165 | 166 | private: 167 | std::shared_ptr _config; 168 | }; 169 | 170 | -------------------------------------------------------------------------------- /CredentialProvider/Configuration.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | 20 | #pragma once 21 | #include "PIConfig.h" 22 | #include "PIResponse.h" 23 | #include "Mode.h" 24 | #include 25 | 26 | class Configuration 27 | { 28 | public: 29 | void Load(); 30 | 31 | void LogConfig(); 32 | 33 | std::string ValidateAcceptLanguage(std::wstring configEntry); 34 | 35 | PIConfig piconfig; 36 | 37 | template 38 | bool IsModeOneOf(Modes... modes) const noexcept 39 | { 40 | return ((mode == modes) || ...); 41 | } 42 | 43 | inline std::string ModeToString(Mode m) 44 | { 45 | switch (m) 46 | { 47 | case Mode::NO_CHANGE: return "NO_CHANGE"; 48 | case Mode::CHANGE_PASSWORD: return "CHANGE_PASSWORD"; 49 | case Mode::USERNAME: return "USERNAME"; 50 | case Mode::PASSWORD: return "PASSWORD"; 51 | case Mode::USERNAMEPASSWORD: return "USERNAMEPASSWORD"; 52 | case Mode::PRIVACYIDEA: return "PRIVACYIDEA"; 53 | case Mode::SEC_KEY_ANY: return "SEC_KEY_ANY"; 54 | case Mode::PASSKEY: return "PASSKEY"; 55 | case Mode::SEC_KEY_REG: return "SEC_KEY_REG"; 56 | case Mode::SEC_KEY_REG_PIN: return "SEC_KEY_REG_PIN"; 57 | case Mode::SEC_KEY_PIN: return "SEC_KEY_PIN"; 58 | case Mode::SEC_KEY_NO_PIN: return "SEC_KEY_NO_PIN"; 59 | case Mode::SEC_KEY_NO_DEVICE: return "SEC_KEY_NO_DEVICE"; 60 | default: return "UNKNOWN_MODE"; 61 | } 62 | } 63 | 64 | bool IsCredentialComplete() const noexcept 65 | { 66 | return !credential.username.empty() && !credential.password.empty() && !credential.domain.empty(); 67 | } 68 | 69 | inline std::string ModeString() 70 | { 71 | return ModeToString(mode); 72 | } 73 | 74 | bool IsPasswordInFirstStep() const noexcept 75 | { 76 | return twoStepSendPassword || usernamePassword; 77 | } 78 | 79 | bool IsAutoLogonConfigured() const noexcept 80 | { 81 | return !autoLogonUsername.empty() && !autoLogonPassword.empty() && !autoLogonDomain.empty(); 82 | } 83 | 84 | bool IsFirstStep() const noexcept 85 | { 86 | return mode == Mode::USERNAME || mode == Mode::USERNAMEPASSWORD || mode == Mode::NO_CHANGE; 87 | } 88 | 89 | Mode GetFirstStepMode() const noexcept 90 | { 91 | if (twoStepSendPassword || usernamePassword) 92 | { 93 | return Mode::USERNAMEPASSWORD; 94 | } 95 | return Mode::USERNAME; 96 | } 97 | 98 | // FIDO2 99 | bool usePasskey = false; // Online 100 | bool useOfflineFIDO = false; // Offline 101 | bool disablePasskey = false; 102 | 103 | std::wstring bitmapPath = L""; 104 | 105 | // Add locales files path 106 | std::wstring localesPath = L""; 107 | std::string language = ""; 108 | 109 | bool usernamePassword = false; 110 | bool twoStepSendPassword = false; 111 | bool twoStepSendEmptyPassword = false; 112 | 113 | bool hideFullName = false; 114 | bool hideDomainName = false; 115 | 116 | bool showDomainHint = false; 117 | bool prefillUsername = false; 118 | 119 | bool showResetLink = false; 120 | 121 | bool debugLog = false; 122 | bool hideFirstStepResponseError = false; 123 | bool noDefault = false; 124 | 125 | int winVerMajor = 0; 126 | int winVerMinor = 0; 127 | int winBuildNr = 0; 128 | 129 | bool pushAuthenticationSuccess = false; 130 | 131 | bool isRemoteSession = false; 132 | 133 | bool doAutoLogon = false; 134 | 135 | // Save the last response and the last response with challenge, in case lastResponse is an error/fail to be able 136 | // to show the challenges again 137 | std::optional lastResponse; 138 | std::optional lastResponseWithChallenge; 139 | std::string lastTransactionId = ""; 140 | 141 | std::wstring excludedAccount = L""; 142 | std::wstring excludedGroup = L""; 143 | std::wstring exludedGroupNetBIOSaddress = L""; 144 | 145 | bool clearFields = true; 146 | bool bypassPrivacyIDEA = false; 147 | 148 | int offlineTreshold = 20; 149 | bool offlineShowInfo = true; 150 | 151 | bool webAuthnPreferred = false; 152 | bool webAuthnOfflineNoPIN = false; 153 | // If true, offer FIDO Authentication in the second step if there is offline data for the user. 154 | // In that case, the link will take the text from the online variant, to look the same to the user, 155 | // but will use the offline data 156 | bool webAuthnOfflineSecondStep = false; 157 | bool webAuthnOfflinePreferred = false; 158 | bool webAuthnOfflineHideFirstStep = false; 159 | 160 | bool otpFailReturnToFirstStep = false; 161 | 162 | // Autologon like https://learn.microsoft.com/en-us/troubleshoot/windows-server/user-profiles-and-logon/turn-on-automatic-logon 163 | std::wstring autoLogonUsername = L""; 164 | std::wstring autoLogonDomain = L""; 165 | std::wstring autoLogonPassword = L""; 166 | 167 | // Track the current state 168 | Mode mode = Mode::NO_CHANGE; 169 | 170 | struct PROVIDER 171 | { 172 | ICredentialProviderEvents* pCredentialProviderEvents = nullptr; 173 | UINT_PTR upAdviseContext = 0; 174 | 175 | CREDENTIAL_PROVIDER_USAGE_SCENARIO cpu = CPUS_INVALID; 176 | DWORD credPackFlags = 0; 177 | 178 | // Possibly read-write 179 | PWSTR* status_text = nullptr; 180 | CREDENTIAL_PROVIDER_STATUS_ICON* status_icon = nullptr; 181 | 182 | // Read-only 183 | wchar_t** field_strings = nullptr; 184 | } provider; 185 | 186 | struct CREDENTIAL 187 | { 188 | std::wstring username = L""; 189 | std::wstring domain = L""; 190 | std::wstring password = L""; 191 | std::wstring otp = L""; 192 | std::wstring upn = L""; 193 | std::wstring fido2PIN = L""; 194 | 195 | bool passwordMustChange = false; 196 | bool passwordChanged = false; 197 | 198 | std::wstring newPassword1 = L""; 199 | std::wstring newPassword2 = L""; 200 | } credential; 201 | }; 202 | -------------------------------------------------------------------------------- /Shared/Shared.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | test 14 | x64 15 | 16 | 17 | 18 | 16.0 19 | Win32Proj 20 | {b8d8378c-0720-4dcc-ba1d-d55bddd97037} 21 | Shared 22 | 10.0 23 | 24 | 25 | 26 | StaticLibrary 27 | true 28 | v143 29 | Unicode 30 | 31 | 32 | StaticLibrary 33 | true 34 | v143 35 | Unicode 36 | 37 | 38 | StaticLibrary 39 | false 40 | v143 41 | true 42 | Unicode 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | true 61 | $(ExecutablePath) 62 | 63 | 64 | true 65 | $(ExecutablePath) 66 | 67 | 68 | false 69 | 70 | 71 | 72 | Level3 73 | true 74 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 75 | true 76 | MultiThreadedDebug 77 | $(SolutionDir)CppClient\CppClient 78 | 79 | 80 | Console 81 | true 82 | 83 | 84 | 85 | 86 | Level3 87 | true 88 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 89 | true 90 | MultiThreadedDebug 91 | $(SolutionDir)CppClient\CppClient 92 | 93 | 94 | Console 95 | true 96 | 97 | 98 | 99 | 100 | Level3 101 | false 102 | true 103 | true 104 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 105 | true 106 | $(SolutionDir)CppClient\CppClient 107 | MultiThreaded 108 | 109 | 110 | Console 111 | true 112 | true 113 | true 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /PrivacyIDEA-CredentialProvider.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34511.84 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CredentialProviderFilter", "CredentialProviderFilter\CredentialProviderFilter.vcxproj", "{E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{702EF40A-4EA9-4927-B46F-A876B7DA17DF}" 9 | ProjectSection(SolutionItems) = preProject 10 | ApacheLicense.rtf = ApacheLicense.rtf 11 | ApacheLicense.txt = ApacheLicense.txt 12 | BUILD.TXT = BUILD.TXT 13 | locales\de.json = locales\de.json 14 | locales\en.json = locales\en.json 15 | locales\es.json = locales\es.json 16 | EndProjectSection 17 | EndProject 18 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CredentialProvider", "CredentialProvider\CredentialProvider.vcxproj", "{2DF895C3-D1B4-4632-8F76-F06670A0D311}" 19 | ProjectSection(ProjectDependencies) = postProject 20 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC} = {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC} 21 | EndProjectSection 22 | EndProject 23 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "WiXSetup", "WiXSetup\WiXSetup.wixproj", "{4D2113B2-C2D3-44D1-BFC7-5502E801872E}" 24 | EndProject 25 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "versioning", "versioning", "{F6FE8812-5A52-4DF8-A56A-9E8E1B4B9EE7}" 26 | ProjectSection(SolutionItems) = preProject 27 | versioning\version.h = versioning\version.h 28 | EndProjectSection 29 | EndProject 30 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppClient", "CppClient\CppClient\CppClient.vcxproj", "{6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}" 31 | EndProject 32 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shared", "Shared\Shared.vcxproj", "{B8D8378C-0720-4DCC-BA1D-D55BDDD97037}" 33 | EndProject 34 | Global 35 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 36 | Debug|x64 = Debug|x64 37 | Debug|x86 = Debug|x86 38 | Release|x64 = Release|x64 39 | Release|x86 = Release|x86 40 | test|x64 = test|x64 41 | test|x86 = test|x86 42 | EndGlobalSection 43 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 44 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.Debug|x64.ActiveCfg = Debug|x64 45 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.Debug|x64.Build.0 = Debug|x64 46 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.Debug|x86.ActiveCfg = Debug|x64 47 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.Debug|x86.Build.0 = Debug|x64 48 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.Release|x64.ActiveCfg = Release|x64 49 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.Release|x64.Build.0 = Release|x64 50 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.Release|x86.ActiveCfg = Release|x64 51 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.Release|x86.Build.0 = Release|x64 52 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.test|x64.ActiveCfg = test|x64 53 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.test|x86.ActiveCfg = test|x64 54 | {E8100BB4-E5F1-47D0-A4FD-4A8C70503D9F}.test|x86.Build.0 = test|x64 55 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.Debug|x64.ActiveCfg = Debug|x64 56 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.Debug|x64.Build.0 = Debug|x64 57 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.Debug|x86.ActiveCfg = Debug|x64 58 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.Debug|x86.Build.0 = Debug|x64 59 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.Release|x64.ActiveCfg = Release|x64 60 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.Release|x64.Build.0 = Release|x64 61 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.Release|x86.ActiveCfg = Release|x64 62 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.Release|x86.Build.0 = Release|x64 63 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.test|x64.ActiveCfg = test|x64 64 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.test|x86.ActiveCfg = test|x64 65 | {2DF895C3-D1B4-4632-8F76-F06670A0D311}.test|x86.Build.0 = test|x64 66 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.Debug|x64.ActiveCfg = Debug|x64 67 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.Debug|x64.Build.0 = Debug|x64 68 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.Debug|x86.ActiveCfg = Debug|x86 69 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.Debug|x86.Build.0 = Debug|x86 70 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.Release|x64.ActiveCfg = Release|x64 71 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.Release|x64.Build.0 = Release|x64 72 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.Release|x86.ActiveCfg = Release|x86 73 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.Release|x86.Build.0 = Release|x86 74 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.test|x64.ActiveCfg = Release|x64 75 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.test|x86.ActiveCfg = Release|x86 76 | {4D2113B2-C2D3-44D1-BFC7-5502E801872E}.test|x86.Build.0 = Release|x86 77 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Debug|x64.ActiveCfg = Debug|x64 78 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Debug|x64.Build.0 = Debug|x64 79 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Debug|x86.ActiveCfg = Debug|x64 80 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Debug|x86.Build.0 = Debug|x64 81 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Release|x64.ActiveCfg = Release|x64 82 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Release|x64.Build.0 = Release|x64 83 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Release|x86.ActiveCfg = Release|x64 84 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.Release|x86.Build.0 = Release|x64 85 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.test|x64.ActiveCfg = test|x64 86 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.test|x64.Build.0 = test|x64 87 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.test|x86.ActiveCfg = test|x64 88 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC}.test|x86.Build.0 = test|x64 89 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.Debug|x64.ActiveCfg = Debug|x64 90 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.Debug|x64.Build.0 = Debug|x64 91 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.Debug|x86.ActiveCfg = Debug|x64 92 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.Debug|x86.Build.0 = Debug|x64 93 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.Release|x64.ActiveCfg = Release|x64 94 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.Release|x64.Build.0 = Release|x64 95 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.Release|x86.ActiveCfg = Release|x64 96 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.Release|x86.Build.0 = Release|x64 97 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.test|x64.ActiveCfg = test|x64 98 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.test|x86.ActiveCfg = test|x64 99 | {B8D8378C-0720-4DCC-BA1D-D55BDDD97037}.test|x86.Build.0 = test|x64 100 | EndGlobalSection 101 | GlobalSection(SolutionProperties) = preSolution 102 | HideSolutionNode = FALSE 103 | EndGlobalSection 104 | GlobalSection(NestedProjects) = preSolution 105 | {F6FE8812-5A52-4DF8-A56A-9E8E1B4B9EE7} = {702EF40A-4EA9-4927-B46F-A876B7DA17DF} 106 | EndGlobalSection 107 | GlobalSection(ExtensibilityGlobals) = postSolution 108 | SolutionGuid = {4CC99074-7674-465D-A290-F234FF84590E} 109 | EndGlobalSection 110 | EndGlobal 111 | -------------------------------------------------------------------------------- /CppClient/CppClient/RegistryReader.cpp: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2019 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | #include "RegistryReader.h" 20 | #include "Convert.h" 21 | #include 22 | #include 23 | #include "Logger.h" 24 | 25 | using namespace std; 26 | 27 | #define MAX_KEY_LENGTH 255 28 | #define MAX_VALUE_NAME 1024 29 | 30 | RegistryReader::RegistryReader(const std::wstring& pathToKey) noexcept 31 | { 32 | path = pathToKey; 33 | } 34 | 35 | bool RegistryReader::GetAll(const std::wstring& pathToKey, std::map& map) noexcept 36 | { 37 | // Open handle to realm-mapping key 38 | HKEY hKey = nullptr; 39 | auto dwRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pathToKey.c_str(), 0, KEY_READ, &hKey); 40 | if (dwRet != ERROR_SUCCESS) 41 | { 42 | PIError("Failed to open registry key " + Convert::ToString(pathToKey) + ", error: " + Convert::LongToHexString(dwRet)); 43 | return false; 44 | } 45 | 46 | WCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name 47 | DWORD cchClassName = MAX_PATH; // size of class string 48 | DWORD cSubKeys = 0; // number of subkeys 49 | DWORD cbMaxSubKey; // longest subkey size 50 | DWORD cchMaxClass; // longest class string 51 | DWORD cValues; // number of values for key 52 | DWORD cchMaxValue; // longest value name 53 | DWORD cbMaxValueData; // longest value data 54 | DWORD cbSecurityDescriptor; // size of security descriptor 55 | FILETIME ftLastWriteTime; // last write time 56 | 57 | DWORD i, retCode; 58 | 59 | WCHAR achValue[MAX_VALUE_NAME]; 60 | DWORD cchValue = MAX_VALUE_NAME; 61 | 62 | // Get the class name and the value count. 63 | retCode = RegQueryInfoKey( 64 | hKey, // key handle 65 | achClass, // buffer for class name 66 | &cchClassName, // size of class string 67 | NULL, // reserved 68 | &cSubKeys, // number of subkeys 69 | &cbMaxSubKey, // longest subkey size 70 | &cchMaxClass, // longest class string 71 | &cValues, // number of values for this key 72 | &cchMaxValue, // longest value name 73 | &cbMaxValueData, // longest value data 74 | &cbSecurityDescriptor, // security descriptor 75 | &ftLastWriteTime); // last write time 76 | 77 | if (cValues) 78 | { 79 | for (i = 0, retCode = ERROR_SUCCESS; i < cValues; i++) 80 | { 81 | cchValue = MAX_VALUE_NAME; 82 | achValue[0] = '\0'; 83 | retCode = RegEnumValueW(hKey, i, 84 | achValue, 85 | &cchValue, 86 | NULL, 87 | NULL, 88 | NULL, 89 | NULL); 90 | if (retCode == ERROR_SUCCESS) 91 | { 92 | wstring value = Convert::ToUpperCase(achValue); 93 | // Get the data for the value 94 | const DWORD SIZE = 1024; 95 | TCHAR szData[SIZE] = _T(""); 96 | DWORD dwValue = SIZE; 97 | DWORD dwType = 0; 98 | DWORD dwRet = 0; 99 | 100 | dwRet = RegQueryValueEx( 101 | hKey, 102 | value.c_str(), 103 | NULL, 104 | &dwType, 105 | (LPBYTE)&szData, 106 | &dwValue); 107 | if (dwRet == ERROR_SUCCESS) 108 | { 109 | if (dwType == REG_SZ) 110 | { 111 | wstring data(szData); 112 | map.try_emplace(value, data); 113 | } 114 | } 115 | } 116 | else 117 | { 118 | PIError("Failed to read registry value at index " + to_string(i) + " in key " + Convert::ToString(pathToKey) + 119 | ", error: " + Convert::LongToHexString(retCode)); 120 | } 121 | } 122 | } 123 | 124 | RegCloseKey(hKey); 125 | return true; 126 | } 127 | 128 | std::wstring RegistryReader::GetWString(std::wstring name) noexcept 129 | { 130 | DWORD dwRet = NULL; 131 | HKEY hKey = nullptr; 132 | dwRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.c_str(), NULL, KEY_QUERY_VALUE, &hKey); 133 | if (dwRet != ERROR_SUCCESS) 134 | { 135 | PIError("Failed to open registry key " + Convert::ToString(path) + ", error: " + Convert::LongToHexString(dwRet)); 136 | return L""; 137 | } 138 | 139 | const DWORD SIZE = 1024; 140 | TCHAR szValue[SIZE] = _T(""); 141 | DWORD dwValue = SIZE; 142 | DWORD dwType = 0; 143 | dwRet = RegQueryValueEx(hKey, name.c_str(), NULL, &dwType, (LPBYTE)&szValue, &dwValue); 144 | if (dwRet != ERROR_SUCCESS) 145 | { 146 | PIError("Failed to read registry value " + Convert::ToString(name) + ", error: " + Convert::LongToHexString(dwRet)); 147 | return L""; 148 | } 149 | 150 | if (dwType != REG_SZ) 151 | { 152 | PIError("Type of registry value " + Convert::ToString(name) + " is not REG_SZ, but " + Convert::LongToHexString(dwType)); 153 | return L""; 154 | } 155 | RegCloseKey(hKey); 156 | hKey = NULL; 157 | return wstring(szValue); 158 | } 159 | 160 | bool RegistryReader::GetBool(std::wstring name) noexcept 161 | { 162 | // Non existing keys evaluate to false. 163 | return GetWString(name) == L"1"; 164 | } 165 | 166 | int RegistryReader::GetInt(std::wstring name) noexcept 167 | { 168 | return _wtoi(GetWString(name).c_str()); // Invalid parameter returns 0 169 | } 170 | 171 | std::vector RegistryReader::GetMultiSZ(const std::wstring& valueName) noexcept 172 | { 173 | HKEY hKey; 174 | LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_READ, &hKey); 175 | if (result != ERROR_SUCCESS) 176 | { 177 | PIError("Failed to open registry key " + Convert::ToString(path) + ", error: " + Convert::LongToHexString(result)); 178 | return std::vector(); 179 | } 180 | 181 | DWORD dwType = REG_MULTI_SZ, dwSize = 0; 182 | result = RegQueryValueEx(hKey, valueName.c_str(), 0, &dwType, 0, &dwSize); 183 | if (result != ERROR_SUCCESS) 184 | { 185 | PIError("Failed to query size of registry value " + Convert::ToString(valueName) + ", error: " + Convert::LongToHexString(result)); 186 | return std::vector(); 187 | } 188 | 189 | std::vector buffer(dwSize); 190 | result = RegQueryValueEx(hKey, valueName.c_str(), 0, &dwType, (LPBYTE)buffer.data(), &dwSize); 191 | if (result != ERROR_SUCCESS) 192 | { 193 | PIError("Failed to read registry value " + Convert::ToString(valueName) + ", error: " + Convert::LongToHexString(result)); 194 | return std::vector(); 195 | } 196 | 197 | std::vector strings; 198 | for (wchar_t* p = buffer.data(); *p != '\0'; p += lstrlen(p) + 1) 199 | { 200 | strings.push_back(p); 201 | } 202 | 203 | return strings; 204 | } 205 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/privacyIDEACredentialProvider.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/privacyIDEACredentialProvider.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/privacyIDEACredentialProvider" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/privacyIDEACredentialProvider" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /CredentialProvider/core/CCredential.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2012 Dominik Pretzsch 4 | ** 2017 NetKnights GmbH 5 | ** 6 | ** Author Dominik Pretzsch 7 | ** Nils Behlen 8 | ** 9 | ** Licensed under the Apache License, Version 2.0 (the "License"); 10 | ** you may not use this file except in compliance with the License. 11 | ** You may obtain a copy of the License at 12 | ** 13 | ** http://www.apache.org/licenses/LICENSE-2.0 14 | ** 15 | ** Unless required by applicable law or agreed to in writing, software 16 | ** distributed under the License is distributed on an "AS IS" BASIS, 17 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | ** See the License for the specific language governing permissions and 19 | ** limitations under the License. 20 | ** 21 | ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 22 | 23 | #pragma once 24 | 25 | #include "Dll.h" 26 | #include "Utilities.h" 27 | #include "Configuration.h" 28 | #include "PrivacyIDEA.h" 29 | #include "FIDODevice.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #define NOT_EMPTY(NAME) \ 38 | (NAME != NULL && NAME[0] != NULL) 39 | 40 | #define ZERO(NAME) \ 41 | SecureZeroMemory(NAME, sizeof(NAME)) 42 | 43 | class CCredential : public IConnectableCredentialProviderCredential 44 | { 45 | public: 46 | // IUnknown 47 | IFACEMETHODIMP_(ULONG) AddRef() noexcept override 48 | { 49 | return ++_cRef; 50 | } 51 | 52 | IFACEMETHODIMP_(ULONG) Release() noexcept override 53 | { 54 | LONG cRef = --_cRef; 55 | if (!cRef) 56 | { 57 | // The Credential is owned by the Provider object 58 | } 59 | return cRef; 60 | } 61 | 62 | #pragma warning( disable : 4838 ) 63 | IFACEMETHODIMP QueryInterface(__in REFIID riid, __deref_out void** ppv) noexcept override 64 | { 65 | static const QITAB qit[] = 66 | { 67 | QITABENT(CCredential, ICredentialProviderCredential), // IID_ICredentialProviderCredential 68 | QITABENT(CCredential, IConnectableCredentialProviderCredential), // IID_IConnectableCredentialProviderCredential 69 | { 0 }, 70 | }; 71 | 72 | return QISearch(this, qit, riid, ppv); 73 | } 74 | public: 75 | // ICredentialProviderCredential 76 | IFACEMETHODIMP Advise(__in ICredentialProviderCredentialEvents* pcpce) override; 77 | IFACEMETHODIMP UnAdvise() override; 78 | 79 | IFACEMETHODIMP SetSelected(__out BOOL* pbAutoLogon) override; 80 | IFACEMETHODIMP SetDeselected() override; 81 | 82 | IFACEMETHODIMP GetFieldState(__in DWORD dwFieldID, 83 | __out CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs, 84 | __out CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis) override; 85 | 86 | IFACEMETHODIMP GetStringValue(__in DWORD dwFieldID, __deref_out PWSTR* ppwsz) override; 87 | IFACEMETHODIMP GetBitmapValue(__in DWORD dwFieldID, __out HBITMAP* phbmp) override; 88 | IFACEMETHODIMP GetCheckboxValue(__in DWORD dwFieldID, __out BOOL* pbChecked, __deref_out PWSTR* ppwszLabel) override; 89 | IFACEMETHODIMP GetComboBoxValueCount(__in DWORD dwFieldID, __out DWORD* pcItems, __out_range(< , *pcItems) DWORD* pdwSelectedItem) override; 90 | IFACEMETHODIMP GetComboBoxValueAt(__in DWORD dwFieldID, __in DWORD dwItem, __deref_out PWSTR* ppwszItem) override; 91 | IFACEMETHODIMP GetSubmitButtonValue(__in DWORD dwFieldID, __out DWORD* pdwAdjacentTo) override; 92 | 93 | IFACEMETHODIMP SetStringValue(__in DWORD dwFieldID, __in PCWSTR pwz) override; 94 | IFACEMETHODIMP SetCheckboxValue(__in DWORD dwFieldID, __in BOOL bChecked) override; 95 | IFACEMETHODIMP SetComboBoxSelectedValue(__in DWORD dwFieldID, __in DWORD dwSelectedItem) override; 96 | IFACEMETHODIMP CommandLinkClicked(__in DWORD dwFieldID) override; 97 | 98 | IFACEMETHODIMP GetSerialization(__out CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr, 99 | __out CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs, 100 | __deref_out_opt PWSTR* ppwszOptionalStatusText, 101 | __out CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon) override; 102 | 103 | IFACEMETHODIMP ReportResult(__in NTSTATUS ntsStatus, 104 | __in NTSTATUS ntsSubstatus, 105 | __deref_out_opt PWSTR* ppwszOptionalStatusText, 106 | __out CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon) override; 107 | 108 | public: 109 | // IConnectableCredentialProviderCredential 110 | IFACEMETHODIMP Connect(__in IQueryContinueWithStatus* pqcws) override; 111 | IFACEMETHODIMP Disconnect() override; 112 | 113 | CCredential(std::shared_ptr c); 114 | virtual ~CCredential(); 115 | 116 | public: 117 | HRESULT Initialize( 118 | __in const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* rgcpfd, 119 | __in const FIELD_STATE_PAIR* rgfsp, 120 | __in_opt PWSTR userName, 121 | __in_opt PWSTR domainName, 122 | __in_opt PWSTR password); 123 | 124 | HRESULT StopPoll(); 125 | 126 | // Called when UnAdvise is called for the Provider. 127 | // This happens when there is inactivity while logging in and the screen goes into "locked mode". 128 | HRESULT FullReset(); 129 | 130 | private: 131 | HRESULT SetMode(Mode mode); 132 | 133 | HRESULT ResetMode(bool resetToFirstStep = false); 134 | 135 | HRESULT SetDomainHint(std::wstring domain); 136 | 137 | HRESULT SetOfflineInfo(std::string username); 138 | 139 | Mode SelectFIDOMode(std::string userVerification = "", bool offline = false); 140 | 141 | void ShowErrorMessage(const std::wstring& message, const HRESULT& code = 0); 142 | 143 | void PushAuthenticationCallback(const PIResponse& response); 144 | 145 | HBITMAP CreateBitmapFromBase64PNG(const std::wstring& base64); 146 | 147 | bool CheckExcludedAccount(); 148 | 149 | HRESULT FIDOAuthentication(IQueryContinueWithStatus* pqcws); 150 | 151 | HRESULT FIDORegistration(IQueryContinueWithStatus* pqcws); 152 | 153 | HRESULT EvaluateResponse(PIResponse& response); 154 | 155 | HRESULT LoadBitmapFromPathOrResource(const std::wstring& bitmapPath, HBITMAP* phbmp); 156 | 157 | HRESULT SetDefaultBitmap(); 158 | 159 | // Waits until a FIDO2 device is found or the search is cancelled. If the search is cancelled, an empty optional is returned 160 | // and _fidoDeviceSearchCancelled is set to true. 161 | // Checks every 200ms if a device is found. Default timeout is 5 minutes. 162 | std::optional WaitForFIDODevice(IQueryContinueWithStatus* pqcws, int timeoutMs = 300000); 163 | 164 | LONG _cRef; 165 | // An array holding the type and name of each field in the tile. 166 | CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR _rgCredProvFieldDescriptors[FID_NUM_FIELDS]; 167 | 168 | // An array holding the state of each field in the tile. 169 | FIELD_STATE_PAIR _rgFieldStatePairs[FID_NUM_FIELDS]; 170 | 171 | // An array holding the string value of each field. This is different from the name of 172 | // the field held in _rgCredProvFieldDescriptors. 173 | wchar_t* _rgFieldStrings[FID_NUM_FIELDS]; 174 | ICredentialProviderCredentialEvents* _pCredProvCredentialEvents = nullptr; 175 | DWORD _dwComboIndex; 176 | PrivacyIDEA _privacyIDEA; 177 | std::shared_ptr _config; 178 | Utilities _util; 179 | std::wstring _initialDomain; 180 | int _lastStatus = S_OK; 181 | bool _privacyIDEASuccess = false; 182 | bool _fidoDeviceSearchCancelled = false; 183 | bool _modeSwitched = false; 184 | std::optional _passkeyChallenge = std::nullopt; 185 | bool _passkeyRegistrationFailed = false; 186 | 187 | // Flag to indicate that the FID_OTP field should be hidden 188 | // TODO should be modes? 189 | bool _pollEnrollmentInProgress = false; 190 | bool _enrollmentInProgress = false; 191 | }; 192 | -------------------------------------------------------------------------------- /CppClient/CppClient/PrivacyIDEA.h: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * 2 | ** 3 | ** Copyright 2025 NetKnights GmbH 4 | ** Author: Nils Behlen 5 | ** 6 | ** Licensed under the Apache License, Version 2.0 (the "License"); 7 | ** you may not use this file except in compliance with the License. 8 | ** You may obtain a copy of the License at 9 | ** 10 | ** http://www.apache.org/licenses/LICENSE-2.0 11 | ** 12 | ** Unless required by applicable law or agreed to in writing, software 13 | ** distributed under the License is distributed on an "AS IS" BASIS, 14 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | ** See the License for the specific language governing permissions and 16 | ** limitations under the License. 17 | ** 18 | ** * * * * * * * * * * * * * * * * * * */ 19 | #pragma once 20 | 21 | #include "PIResponse.h" 22 | #include "JsonParser.h" 23 | #include "OfflineHandler.h" 24 | #include "Logger.h" 25 | #include "Endpoint.h" 26 | #include "PIConfig.h" 27 | #include "FIDOSignResponse.h" 28 | #include "FIDORegistrationResponse.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | constexpr auto PI_ENDPOINT_VALIDATE_CHECK = "/validate/check"; 36 | constexpr auto PI_ENDPOINT_POLLTRANSACTION = "/validate/polltransaction"; 37 | constexpr auto PI_ENDPOINT_OFFLINE_REFILL = "/validate/offlinerefill"; 38 | constexpr auto PI_ENDPOINT_VALIDATE_INITIALIZE = "/validate/initialize"; 39 | 40 | #define PI_ERROR_WRONG_PARAMETER ((HRESULT)0x88809011) 41 | 42 | constexpr auto PI_ERR_AUTH_FAILED = 0x88809099; 43 | 44 | class PrivacyIDEA 45 | { 46 | public: 47 | PrivacyIDEA(const PIConfig& config) : 48 | _config(config), 49 | _endpoint(config), 50 | offlineHandler(config.offlineFilePath, config.offlineTryWindow) 51 | {}; 52 | 53 | PrivacyIDEA& operator=(const PrivacyIDEA& privacyIDEA) = delete; 54 | 55 | /// 56 | /// Authenticate using the /validate/check endpoint. The server response is written to responseObj. 57 | /// 58 | /// 59 | /// 60 | /// 61 | /// This will be filled with the response of the server if no error occurred. 62 | /// Optional to reference a challenge that was triggered before. If this is empty, it will not be send. 63 | /// Optional UPN to use instead of username. If this is set, it will be send as is instead of the username+domain. 64 | /// S_OK if the request was processed correctly. Possible error codes: PI_ERROR_ENDPOINT_SETUP, PI_ERROR_SERVER_UNAVAILABLE, PI_JSON_PARSE_ERROR 65 | HRESULT ValidateCheck( 66 | const std::wstring& username, 67 | const std::wstring& domain, 68 | const std::wstring& otp, 69 | PIResponse& responseObj, 70 | const std::string& transactionId = std::string(), 71 | const std::wstring& upn = std::wstring(), 72 | const std::map& headers = std::map()); 73 | 74 | /// 75 | /// Authenticate with WebAuthn using the /validate/check endpoint. The server response is written to responseObj. 76 | /// 77 | /// 78 | /// 79 | /// 80 | /// 81 | /// This will be filled with the response of the server if no error occurred 82 | /// Required for this function. WebAuthn is always challenge-response 83 | /// S_OK if the request was processed correctly. Possible error codes: PI_ERROR_ENDPOINT_SETUP, PI_ERROR_SERVER_UNAVAILABLE, PI_JSON_PARSE_ERROR 84 | HRESULT ValidateCheckFIDO(const std::wstring& username, 85 | const std::wstring& domain, const FIDOSignResponse & fidoSignResponse, 86 | const std::string& origin, 87 | PIResponse& response, 88 | const std::string& transactionId, 89 | const std::wstring& upn = std::wstring()); 90 | 91 | /// 92 | /// 93 | /// 94 | /// 95 | /// 96 | /// 97 | /// 98 | /// 99 | /// 100 | HRESULT ValidateCheckCompletePasskeyRegistration( 101 | const std::string& transactionId, 102 | const std::string& serial, 103 | const std::wstring& username, 104 | const std::wstring& domain, 105 | FIDORegistrationResponse registrationResponse, 106 | const std::string& origin, 107 | PIResponse& piresponse); 108 | 109 | /// 110 | /// 111 | /// 112 | /// 113 | /// 114 | /// 115 | HRESULT ValidateInitialize(PIResponse& response, const std::string & type = "passkey"); 116 | 117 | /// 118 | /// Try to validate the given OTP value with the offline data for the user. 119 | /// 120 | /// 121 | /// 122 | /// S_OK, E_FAIL, PI_OFFLINE_DATA_NO_OTPS_LEFT, PI_OFFLINE_NO_OFFLINE_DATA 123 | HRESULT OfflineCheck(const std::wstring& username, const std::wstring& otp, __out std::string& serialUsed); 124 | 125 | /// 126 | /// Try to refill offline OTP values with a request to /validate/offlinerefill. 127 | /// 128 | /// 129 | /// 130 | /// S_OK, E_FAIL, PI_JSON_PARSE_ERROR, PI_ERROR_ENDPOINT_SETUP, PI_ERROR_SERVER_UNAVAILABLE 131 | HRESULT OfflineRefill(const std::wstring& username, const std::wstring& lastOTP, const std::string& serial); 132 | 133 | HRESULT OfflineRefillFIDO(const std::wstring& username, const std::string& serial); 134 | 135 | bool StopPoll(); 136 | 137 | // 138 | // Poll for the given transaction asynchronously. When polling returns success, the transaction is finalized automatically 139 | // according to https://privacyidea.readthedocs.io/en/latest/configuration/authentication_modes.html#outofband-mode 140 | // After that, the callback function is called with the result 141 | // 142 | void PollTransactionAsync(std::wstring username, std::wstring domain, std::wstring upn, std::string transactionId, 143 | std::function callback); 144 | 145 | // 146 | // Poll for a transaction_id. If this returns success, the transaction must be finalized by calling validate/check with the username, transaction_id and an EMPTY pass parameter. 147 | // https://privacyidea.readthedocs.io/en/latest/configuration/authentication_modes.html#outofband-mode 148 | // 149 | bool PollTransaction(std::string transactionId); 150 | 151 | bool OfflineFIDODataExistsFor(std::wstring username); 152 | 153 | bool CancelEnrollmentViaMultichallenge(std::string transactionId); 154 | 155 | OfflineHandler offlineHandler; 156 | 157 | /// 158 | /// Return an offline FIDO2 sign request if offline FIDO2 token data is available. For every token, the credential_id will be in 159 | /// allowed_credential 160 | /// 161 | /// std::optional 162 | std::optional GetOfflineFIDOSignRequest(); 163 | 164 | private: 165 | HRESULT AppendRealm(std::wstring domain, std::map& parameters); 166 | 167 | std::string SendRequestWithFallback( 168 | const std::string& endpoint, 169 | const std::map& parameters, 170 | const std::map& headers, 171 | RequestMethod method); 172 | 173 | HRESULT EvaluateResponse(std::string response, _Inout_ PIResponse& responseObj); 174 | 175 | void PollThread(const std::wstring& username, const std::wstring& domain, const std::wstring& upn, const std::string& transactionId, 176 | std::function callback); 177 | 178 | Endpoint _endpoint; 179 | PIConfig _config; 180 | 181 | std::atomic _runPoll = false; 182 | JsonParser _parser; 183 | }; 184 | 185 | -------------------------------------------------------------------------------- /CppClient/CppClient/CppClient.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | test 14 | x64 15 | 16 | 17 | 18 | 16.0 19 | {6DB4CA8D-7529-4310-A4CF-F3DED2D143CC} 20 | PrivacyIDEACppClientCore 21 | 10.0 22 | CppClient 23 | 24 | 25 | 26 | StaticLibrary 27 | true 28 | v143 29 | Unicode 30 | 31 | 32 | StaticLibrary 33 | true 34 | v143 35 | Unicode 36 | 37 | 38 | StaticLibrary 39 | false 40 | v143 41 | true 42 | Unicode 43 | Spectre 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | true 62 | $(SolutionDir)CppClient\CppClient\$(Platform)\$(Configuration)\ 63 | 64 | 65 | true 66 | $(SolutionDir)CppClient\CppClient\$(Platform)\$(Configuration)\ 67 | 68 | 69 | false 70 | $(SolutionDir)CppClient\CppClient\$(Platform)\$(Configuration)\ 71 | 72 | 73 | 74 | Level4 75 | false 76 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 77 | true 78 | MultiThreadedDebug 79 | ProgramDatabase 80 | $(SolutionDir)CppClient\nlohmann\x64\include;$(SolutionDir)\libfido2-1.15.0-nfc-enabled\include;%(AdditionalIncludeDirectories) 81 | stdcpp17 82 | 83 | 84 | $(SolutionDir)libfido2-1.15.0-nfc-enabled\static;%(AdditionalLibraryDirectories) 85 | fido2.lib;crypt32.lib;zlib1.lib;crypto.lib;cbor.lib;Ws2_32.lib;hid.lib;setupapi.lib;Winscard.lib 86 | 87 | 88 | 89 | 90 | Level4 91 | false 92 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | true 94 | MultiThreadedDebug 95 | ProgramDatabase 96 | $(SolutionDir)CppClient\nlohmann\x64\include;$(SolutionDir)\libfido2-1.15.0-nfc-enabled\include;%(AdditionalIncludeDirectories) 97 | 98 | 99 | $(SolutionDir)libfido2-1.15.0-nfc-enabled\static;%(AdditionalLibraryDirectories) 100 | fido2.lib;crypt32.lib;zlib1.lib;crypto.lib;cbor.lib;Ws2_32.lib;hid.lib;setupapi.lib;Winscard.lib 101 | 102 | 103 | 104 | 105 | Level4 106 | false 107 | false 108 | false 109 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 110 | true 111 | MultiThreaded 112 | stdcpp17 113 | MaxSpeed 114 | $(SolutionDir)CppClient\nlohmann\x64\include;$(SolutionDir)\libfido2-1.15.0-nfc-enabled\include;%(AdditionalIncludeDirectories) 115 | 116 | 117 | Console 118 | true 119 | true 120 | true 121 | 122 | 123 | fido2.lib;crypt32.lib;zlib1.lib;crypto.lib;cbor.lib;Ws2_32.lib;hid.lib;setupapi.lib;Winscard.lib 124 | 125 | 126 | $(SolutionDir)libfido2-1.15.0-nfc-enabled\static;%(AdditionalLibraryDirectories) 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | --------------------------------------------------------------------------------