summaryrefslogtreecommitdiffstats
path: root/winpr/tools
diff options
context:
space:
mode:
Diffstat (limited to 'winpr/tools')
-rw-r--r--winpr/tools/CMakeLists.txt149
-rw-r--r--winpr/tools/WinPR-toolsConfig.cmake.in12
-rw-r--r--winpr/tools/hash-cli/CMakeLists.txt59
-rw-r--r--winpr/tools/hash-cli/hash.c216
-rw-r--r--winpr/tools/hash-cli/winpr-hash.1.in42
-rw-r--r--winpr/tools/makecert-cli/CMakeLists.txt63
-rw-r--r--winpr/tools/makecert-cli/main.c45
-rw-r--r--winpr/tools/makecert-cli/winpr-makecert.1.in116
-rw-r--r--winpr/tools/makecert/CMakeLists.txt49
-rw-r--r--winpr/tools/makecert/makecert.c1165
-rw-r--r--winpr/tools/winpr-tools.pc.in15
11 files changed, 1931 insertions, 0 deletions
diff --git a/winpr/tools/CMakeLists.txt b/winpr/tools/CMakeLists.txt
new file mode 100644
index 0000000..ed7734d
--- /dev/null
+++ b/winpr/tools/CMakeLists.txt
@@ -0,0 +1,149 @@
+# WinPR: Windows Portable Runtime
+# winpr cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+# Copyright 2016 Thincast Technologies GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Soname versioning - use winpr version
+set(WINPR_TOOLS_VERSION_MAJOR "${WINPR_VERSION_MAJOR}")
+set(WINPR_TOOLS_VERSION_MINOR "${WINPR_VERSION_MINOR}")
+set(WINPR_TOOLS_VERSION_REVISION "${WINPR_VERSION_REVISION}")
+
+set(WINPR_TOOLS_API_VERSION "${WINPR_TOOLS_VERSION_MAJOR}")
+set(WINPR_TOOLS_VERSION "${WINPR_TOOLS_VERSION_MAJOR}.${WINPR_TOOLS_VERSION_MINOR}.${WINPR_TOOLS_VERSION_REVISION}")
+set(WINPR_TOOLS_VERSION_FULL "${WINPR_TOOLS_VERSION}")
+set(WINPR_TOOLS_API_VERSION "${WINPR_TOOLS_VERSION_MAJOR}")
+
+set(WINPR_TOOLS_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+set(WINPR_TOOLS_SRCS "")
+set(WINPR_TOOLS_LIBS "")
+set(WINPR_TOOLS_INCLUDES "")
+set(WINPR_TOOLS_DEFINITIONS "")
+
+macro (winpr_tools_module_add)
+ file (RELATIVE_PATH _relPath "${WINPR_TOOLS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
+ foreach (_src ${ARGN})
+ if (_relPath)
+ list (APPEND WINPR_TOOLS_SRCS "${_relPath}/${_src}")
+ else()
+ list (APPEND WINPR_TOOLS_SRCS "${_src}")
+ endif()
+ endforeach()
+ if (_relPath)
+ set (WINPR_TOOLS_SRCS ${WINPR_TOOLS_SRCS} PARENT_SCOPE)
+ endif()
+endmacro()
+
+macro (winpr_tools_include_directory_add)
+ file (RELATIVE_PATH _relPath "${WINPR_TOOLS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
+ foreach (_inc ${ARGN})
+ if (IS_ABSOLUTE ${_inc})
+ list (APPEND WINPR_TOOLS_INCLUDES "${_inc}")
+ else()
+ if (_relPath)
+ list (APPEND WINPR_TOOLS_INCLUDES "${_relPath}/${_inc}")
+ else()
+ list (APPEND WINPR_TOOLS_INCLUDES "${_inc}")
+ endif()
+ endif()
+ endforeach()
+ if (_relPath)
+ set (WINPR_TOOLS_INCLUDES ${WINPR_TOOLS_INCLUDES} PARENT_SCOPE)
+ endif()
+endmacro()
+
+macro (winpr_tools_library_add)
+ foreach (_lib ${ARGN})
+ list (APPEND WINPR_TOOLS_LIBS "${_lib}")
+ endforeach()
+ set (WINPR_TOOLS_LIBS ${WINPR_TOOLS_LIBS} PARENT_SCOPE)
+endmacro()
+
+macro (winpr_tools_definition_add)
+ foreach (_define ${ARGN})
+ list (APPEND WINPR_TOOLS_DEFINITONS "${_define}")
+ endforeach()
+ set (WINPR_TOOLS_DEFINITONS ${WINPR_TOOLS_DEFINITONS} PARENT_SCOPE)
+endmacro()
+
+add_subdirectory(makecert)
+
+set(MODULE_NAME winpr-tools)
+list(REMOVE_DUPLICATES WINPR_TOOLS_DEFINITIONS)
+list(REMOVE_DUPLICATES WINPR_TOOLS_LIBS)
+list(REMOVE_DUPLICATES WINPR_TOOLS_INCLUDES)
+include_directories(${WINPR_TOOLS_INCLUDES})
+
+# On windows create dll version information.
+# Vendor, product and year are already set in top level CMakeLists.txt
+if (WIN32)
+ set (RC_VERSION_MAJOR ${WINPR_VERSION_MAJOR})
+ set (RC_VERSION_MINOR ${WINPR_VERSION_MINOR})
+ set (RC_VERSION_BUILD ${WINPR_VERSION_REVISION})
+ set (RC_VERSION_FILE "${CMAKE_SHARED_LIBRARY_PREFIX}${MODULE_NAME}${WINPR_TOOLS_API_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}" )
+
+ configure_file(
+ ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/version.rc
+ @ONLY)
+
+ set (WINPR_TOOLS_SRCS ${WINPR_TOOLS_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+endif()
+
+add_library(${MODULE_NAME} ${WINPR_TOOLS_SRCS})
+set_target_properties(${MODULE_NAME} PROPERTIES LINKER_LANGUAGE C)
+set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${WINPR_TOOLS_API_VERSION})
+if (WITH_LIBRARY_VERSIONING)
+ set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_TOOLS_VERSION} SOVERSION ${WINPR_TOOLS_API_VERSION})
+endif()
+
+add_definitions(${WINPR_DEFINITIONS})
+target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include/winpr${WINPR_VERSION_MAJOR}>)
+target_link_libraries(${MODULE_NAME} PRIVATE ${WINPR_TOOLS_LIBS})
+
+install(TARGETS ${MODULE_NAME} COMPONENT libraries EXPORT WinPR-toolsTargets
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+if (WITH_DEBUG_SYMBOLS AND MSVC AND BUILD_SHARED_LIBS)
+ get_target_property(OUTPUT_FILENAME ${MODULE_NAME} OUTPUT_NAME)
+ install(FILES ${CMAKE_PDB_BINARY_DIR}/${OUTPUT_FILENAME}.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT symbols)
+endif()
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Tools")
+
+# Add all command line utilities
+add_subdirectory(makecert-cli)
+add_subdirectory(hash-cli)
+
+include(pkg-config-install-prefix)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/winpr-tools.pc.in ${CMAKE_CURRENT_BINARY_DIR}/winpr-tools${WINPR_TOOLS_VERSION_MAJOR}.pc @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/winpr-tools${WINPR_TOOLS_VERSION_MAJOR}.pc DESTINATION ${PKG_CONFIG_PC_INSTALL_DIR})
+
+export(PACKAGE ${MODULE_NAME})
+
+SetFreeRDPCMakeInstallDir(WINPR_CMAKE_INSTALL_DIR "WinPR-tools${WINPR_VERSION_MAJOR}")
+
+configure_package_config_file(WinPR-toolsConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/WinPR-toolsConfig.cmake
+ INSTALL_DESTINATION ${WINPR_CMAKE_INSTALL_DIR}
+ PATH_VARS WINPR_INCLUDE_DIR)
+
+write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/WinPR-toolsConfigVersion.cmake
+ VERSION ${WINPR_VERSION} COMPATIBILITY SameMajorVersion)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/WinPR-toolsConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/WinPR-toolsConfigVersion.cmake
+ DESTINATION ${WINPR_CMAKE_INSTALL_DIR})
+
+install(EXPORT WinPR-toolsTargets DESTINATION ${WINPR_CMAKE_INSTALL_DIR})
diff --git a/winpr/tools/WinPR-toolsConfig.cmake.in b/winpr/tools/WinPR-toolsConfig.cmake.in
new file mode 100644
index 0000000..65f9f48
--- /dev/null
+++ b/winpr/tools/WinPR-toolsConfig.cmake.in
@@ -0,0 +1,12 @@
+include(CMakeFindDependencyMacro)
+find_dependency(WinPR @FREERDP_VERSION@)
+
+@PACKAGE_INIT@
+
+set(WinPR-tools_VERSION_MAJOR "@WINPR_VERSION_MAJOR@")
+set(WinPR-tools_VERSION_MINOR "@WINPR_VERSION_MINOR@")
+set(WinPR-tools_VERSION_REVISION "@WINPR_VERSION_REVISION@")
+
+set_and_check(WinPR-tools_INCLUDE_DIR "@PACKAGE_WINPR_INCLUDE_DIR@")
+
+include("${CMAKE_CURRENT_LIST_DIR}/WinPR-toolsTargets.cmake")
diff --git a/winpr/tools/hash-cli/CMakeLists.txt b/winpr/tools/hash-cli/CMakeLists.txt
new file mode 100644
index 0000000..8f583d3
--- /dev/null
+++ b/winpr/tools/hash-cli/CMakeLists.txt
@@ -0,0 +1,59 @@
+# WinPR: Windows Portable Runtime
+# winpr-hash cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(MODULE_NAME "winpr-hash")
+set(MODULE_PREFIX "WINPR_TOOLS_HASH")
+
+set(${MODULE_PREFIX}_SRCS
+ hash.c)
+
+# On windows create dll version information.
+# Vendor, product and year are already set in top level CMakeLists.txt
+if (WIN32)
+ set(RC_VERSION_MAJOR ${WINPR_VERSION_MAJOR})
+ set(RC_VERSION_MINOR ${WINPR_VERSION_MINOR})
+ set(RC_VERSION_BUILD ${WINPR_VERSION_REVISION})
+ set(RC_VERSION_FILE "${MODULE_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
+
+ configure_file(
+ ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/version.rc
+ @ONLY)
+
+ set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+endif()
+
+add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+set(${MODULE_PREFIX}_LIBS winpr)
+
+set(MANPAGE_NAME "${MODULE_NAME}")
+if (WITH_BINARY_VERSIONING)
+ set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME "${MODULE_NAME}${WINPR_API_VERSION}")
+ set(MANPAGE_NAME "${MODULE_NAME}${WINPR_API_VERSION}")
+endif()
+target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
+
+install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT tools EXPORT WinPRTargets)
+
+if (WITH_DEBUG_SYMBOLS AND MSVC)
+ install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT symbols)
+endif()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Tools")
+configure_file(winpr-hash.1.in ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1)
+install_freerdp_man(${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1 1)
diff --git a/winpr/tools/hash-cli/hash.c b/winpr/tools/hash-cli/hash.c
new file mode 100644
index 0000000..b98f8e9
--- /dev/null
+++ b/winpr/tools/hash-cli/hash.c
@@ -0,0 +1,216 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Hashing Tool
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <winpr/ntlm.h>
+#include <winpr/ssl.h>
+#include <winpr/assert.h>
+
+/**
+ * Define NTOWFv1(Password, User, Domain) as
+ * MD4(UNICODE(Password))
+ * EndDefine
+ *
+ * Define LMOWFv1(Password, User, Domain) as
+ * ConcatenationOf(DES(UpperCase(Password)[0..6], "KGS!@#$%"),
+ * DES(UpperCase(Password)[7..13], "KGS!@#$%"))
+ * EndDefine
+ *
+ * Define NTOWFv2(Password, User, Domain) as
+ * HMAC_MD5(MD4(UNICODE(Password)),
+ * UNICODE(ConcatenationOf(UpperCase(User), Domain)))
+ * EndDefine
+ *
+ * Define LMOWFv2(Password, User, Domain) as
+ * NTOWFv2(Password, User, Domain)
+ * EndDefine
+ *
+ */
+
+static WINPR_NORETURN(void usage_and_exit(void))
+{
+ printf("winpr-hash: NTLM hashing tool\n");
+ printf("Usage: winpr-hash -u <username> -p <password> [-d <domain>] [-f <_default_,sam>] [-v "
+ "<_1_,2>]\n");
+ exit(1);
+}
+
+int main(int argc, char* argv[])
+{
+ int index = 1;
+ int format = 0;
+ unsigned long version = 1;
+ BYTE NtHash[16];
+ char* User = NULL;
+ size_t UserLength = 0;
+ char* Domain = NULL;
+ size_t DomainLength = 0;
+ char* Password = NULL;
+ size_t PasswordLength = 0;
+ errno = 0;
+
+ while (index < argc)
+ {
+ if (strcmp("-d", argv[index]) == 0)
+ {
+ index++;
+
+ if (index == argc)
+ {
+ printf("missing domain\n\n");
+ usage_and_exit();
+ }
+
+ Domain = argv[index];
+ }
+ else if (strcmp("-u", argv[index]) == 0)
+ {
+ index++;
+
+ if (index == argc)
+ {
+ printf("missing username\n\n");
+ usage_and_exit();
+ }
+
+ User = argv[index];
+ }
+ else if (strcmp("-p", argv[index]) == 0)
+ {
+ index++;
+
+ if (index == argc)
+ {
+ printf("missing password\n\n");
+ usage_and_exit();
+ }
+
+ Password = argv[index];
+ }
+ else if (strcmp("-v", argv[index]) == 0)
+ {
+ index++;
+
+ if (index == argc)
+ {
+ printf("missing version parameter\n\n");
+ usage_and_exit();
+ }
+
+ version = strtoul(argv[index], NULL, 0);
+
+ if (((version != 1) && (version != 2)) || (errno != 0))
+ {
+ printf("unknown version %lu \n\n", version);
+ usage_and_exit();
+ }
+ }
+ else if (strcmp("-f", argv[index]) == 0)
+ {
+ index++;
+
+ if (index == argc)
+ {
+ printf("missing format\n\n");
+ usage_and_exit();
+ }
+
+ if (strcmp("default", argv[index]) == 0)
+ format = 0;
+ else if (strcmp("sam", argv[index]) == 0)
+ format = 1;
+ }
+ else if (strcmp("-h", argv[index]) == 0)
+ {
+ usage_and_exit();
+ }
+
+ index++;
+ }
+
+ if ((!User) || (!Password))
+ {
+ printf("missing username or password\n\n");
+ usage_and_exit();
+ }
+ winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
+
+ UserLength = strlen(User);
+ PasswordLength = strlen(Password);
+ DomainLength = (Domain) ? strlen(Domain) : 0;
+
+ WINPR_ASSERT(UserLength <= UINT32_MAX);
+ WINPR_ASSERT(PasswordLength <= UINT32_MAX);
+ WINPR_ASSERT(DomainLength <= UINT32_MAX);
+
+ if (version == 2)
+ {
+ if (!Domain)
+ {
+ printf("missing domain (version 2 requires a domain to specified)\n\n");
+ usage_and_exit();
+ }
+
+ if (!NTOWFv2A(Password, (UINT32)PasswordLength, User, (UINT32)UserLength, Domain,
+ (UINT32)DomainLength, NtHash))
+ {
+ fprintf(stderr, "Hash creation failed\n");
+ return 1;
+ }
+ }
+ else
+ {
+ if (!NTOWFv1A(Password, (UINT32)PasswordLength, NtHash))
+ {
+ fprintf(stderr, "Hash creation failed\n");
+ return 1;
+ }
+ }
+
+ if (format == 0)
+ {
+ for (int index = 0; index < 16; index++)
+ printf("%02" PRIx8 "", NtHash[index]);
+
+ printf("\n");
+ }
+ else if (format == 1)
+ {
+ printf("%s:", User);
+
+ if (DomainLength > 0)
+ printf("%s:", Domain);
+ else
+ printf(":");
+
+ printf(":");
+
+ for (int index = 0; index < 16; index++)
+ printf("%02" PRIx8 "", NtHash[index]);
+
+ printf(":::");
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/winpr/tools/hash-cli/winpr-hash.1.in b/winpr/tools/hash-cli/winpr-hash.1.in
new file mode 100644
index 0000000..0b1f36a
--- /dev/null
+++ b/winpr/tools/hash-cli/winpr-hash.1.in
@@ -0,0 +1,42 @@
+.TH @MANPAGE_NAME@ 1 2017-01-11 "@WINPR_VERSION_FULL@" "FreeRDP"
+.SH NAME
+@MANPAGE_NAME@ \- NTLM hashing tool
+.SH SYNOPSIS
+.B @MANPAGE_NAME@
+\fB-u\fP username
+\fB-p\fP password
+[\fB-d\fP domain]
+[\fB-f\fP { \fIdefault\fP | sam }]
+[\fB-v\fP { \fI1\fP | 2 }]
+.SH DESCRIPTION
+.B @MANPAGE_NAME@
+is a small utility that can be used to create a NTLM hash from a username and password pair. The created hash can be outputed as plain hash or in SAM format.
+.SH OPTIONS
+.IP "-u username"
+The username to use.
+.IP "-p password"
+Password to use.
+.IP "-d domain"
+A optional parameter to specify the domain of the user.
+.IP "-f format"
+Specify the output format. The \fIdefault\fP outputs only the plain NTLM
+hash. The second output format available is \fIsam\fP which outputs the
+created hash in a format that it can be used in SAM file:
+
+user:domain::hash:::
+.IP "-v version"
+Version allows it to specify the NTLM version to use. The default is to use version 1. In case
+version 2 is used a domain needs to be specified.
+.SH EXAMPLES
+@MANPAGE_NAME@ -u \fIuser\fP -p \fIpassword\fP -d \fIdomain\fP -f \fIsam\fP -v \fI2\fP
+
+Create a version \fI2\fP NTLM hash for \fIuser\fP with \fIdomain\fP and \fIpassword\fP and output it in \fIsam\fP format.
+.SH EXIT STATUS
+.TP
+.B 0
+Successful program execution.
+.TP
+.B 1
+Missing or invalid arguments.
+.SH AUTHOR
+FreeRDP <team@freerdp.com>
diff --git a/winpr/tools/makecert-cli/CMakeLists.txt b/winpr/tools/makecert-cli/CMakeLists.txt
new file mode 100644
index 0000000..e92d6f2
--- /dev/null
+++ b/winpr/tools/makecert-cli/CMakeLists.txt
@@ -0,0 +1,63 @@
+# WinPR: Windows Portable Runtime
+# winpr-makecert cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+# Copyright 2016 Thincast Technologies GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(MODULE_NAME "winpr-makecert")
+set(MODULE_PREFIX "WINPR_MAKECERT")
+
+set(${MODULE_PREFIX}_SRCS
+ main.c)
+
+# On windows create dll version information.
+# Vendor, product and year are already set in top level CMakeLists.txt
+if (WIN32)
+ set(RC_VERSION_MAJOR ${WINPR_VERSION_MAJOR})
+ set(RC_VERSION_MINOR ${WINPR_VERSION_MINOR})
+ set(RC_VERSION_BUILD ${WINPR_VERSION_REVISION})
+ set(RC_VERSION_FILE "${MODULE_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
+
+ configure_file(
+ ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/version.rc
+ @ONLY)
+
+ set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+endif()
+
+add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+set(${MODULE_PREFIX}_LIBS winpr-tools)
+
+target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS} winpr)
+
+set(MANPAGE_NAME ${MODULE_NAME})
+if (WITH_BINARY_VERSIONING)
+ set_target_properties(${MODULE_NAME}
+ PROPERTIES
+ OUTPUT_NAME "${MODULE_NAME}${WINPR_API_VERSION}"
+ )
+ set(MANPAGE_NAME ${MODULE_NAME}${WINPR_API_VERSION})
+endif()
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Tools")
+
+install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT tools EXPORT WinPRTargets)
+if (WITH_DEBUG_SYMBOLS AND MSVC)
+ install(FILES ${CMAKE_PDB_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT symbols)
+endif()
+
+configure_file(winpr-makecert.1.in ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1)
+install_freerdp_man(${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1 1)
diff --git a/winpr/tools/makecert-cli/main.c b/winpr/tools/makecert-cli/main.c
new file mode 100644
index 0000000..fa01f7e
--- /dev/null
+++ b/winpr/tools/makecert-cli/main.c
@@ -0,0 +1,45 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * makecert replacement
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <winpr/crt.h>
+#include <winpr/cmdline.h>
+#include <winpr/sysinfo.h>
+
+#include <winpr/tools/makecert.h>
+
+int main(int argc, char* argv[])
+{
+ MAKECERT_CONTEXT* context = NULL;
+ int ret = 0;
+
+ context = makecert_context_new();
+ if (!context)
+ return 1;
+
+ if (makecert_context_process(context, argc, argv) < 0)
+ ret = 1;
+
+ makecert_context_free(context);
+
+ return ret;
+}
diff --git a/winpr/tools/makecert-cli/winpr-makecert.1.in b/winpr/tools/makecert-cli/winpr-makecert.1.in
new file mode 100644
index 0000000..a50c82c
--- /dev/null
+++ b/winpr/tools/makecert-cli/winpr-makecert.1.in
@@ -0,0 +1,116 @@
+.de URL
+\\$2 \(laURL: \\$1 \(ra\\$3
+..
+.if \n[.g] .mso www.tmac
+.TH @MANPAGE_NAME@ 1 2017-01-11 "@WINPR_VERSION_FULL@" "FreeRDP"
+.SH NAME
+@MANPAGE_NAME@ \- A tool to create X.509 certificates.
+.SH SYNOPSIS
+.B @MANPAGE_NAME@
+[\fB-rdp\fP]
+[\fB-silent\fP]
+[\fB-live\fP]
+[\fB-format\fP { \fIcrt\fP | \fIpem\fP | \fIpfx\fP }]
+[\fB-p\fP password]
+[\fB-n\fP common_name]
+[\fB-y\fP years]
+[\fB-m\fP months]
+[\fB-len\fP length]
+[\fB-#\fP serial]
+[\fB-a\fP { \fImd5\fP | \fIsha1\fP | \fIsha256\fP | \fIs384\fP | \fIsha512\fP }]
+[\fB-path\fP outputpath]
+[outputname]
+.SH DESCRIPTION
+.B @MANPAGE_NAME@
+is a tool for generating X.509 certificates modeled after the Windows command
+MakeCert. @MANPAGE_NAME@ aims to be command line compatible with MakeCert
+however not all options are supported or implemented yet.
+
+Unimplemented features are not described here. They are marked as "Unsupported"
+in @MANPAGE_NAME@s help.
+
+In contrast to it's Windows counterpart @MANPAGE_NAME@ does, unless the
+\fB\-live\fP option is given, always creates and save a certificate.
+If \fIoutputname\fP isn't set it is tried to determine the host name of the
+computer the command is run on.
+.br
+\fBWarning:\fP if the file already exists it will be overwritten without asking.
+
+Without further options the generated certificates have the following properties:
+
+* 2048 bit long
+.br
+* sha256 as hash algorithm
+.br
+* the detected host name is used as common name
+.br
+* a time stamp is used as serial number
+.br
+* validity period of one year
+.br
+* saved in the current working directory in crt format
+.SH OPTIONS
+.IP "-rdp"
+Dummy parameter. Can be used to quickly generate a certificate with default
+properties without specifying any further parameters.
+.IP "-silent"
+Don't print the generated certificate to stdout.
+.IP "-f format"
+Three formats are supported: crt, pem and pfx.
+.br
+\fIcrt\fP outputs the key and the certificate in a separate file each with the file
+endings .key and .crt.
+.br
+\fIpem\fP outputs the key and certificate into a single file with the file ending pem.
+.br
+And \fIpfx\fP outputs key and certificate into a pkcs12 file with the ending .pfx.
+.IP "-p password"
+Password to use if the pfx format is used as format.
+.IP "-live"
+Don't write the key/certificate to disk. When used from the command line this
+can be thought as "dummy" mode.
+.IP "-n common_name"
+The common name to use in the certificate.
+.IP "-m months"
+Validity period in months.
+.IP "-y years"
+Validity period in years. If months and years are specified the specified
+month parameter will take precedence.
+.IP "-len length"
+Key length in bits to use.
+.IP "-a { \fImd5\fP | \fIsha1\fP | \fIsha256\fP | \fIs384\fP | \fIsha512\fP }"
+The hashing algorithm to use.
+.IP "-# serial"
+The serial number to use for the certificate.
+.IP "-path"
+A directory where the certificate should be created in.
+.IP "outputname"
+The base name of the created file(s). A suffix, the format specific suffix is
+appended to this name.
+.SH EXAMPLES
+@MANPAGE_NAME@ -rdp
+
+Creates a certificate with the default properties, saved to a file in the
+current working directory in crt format named like the host. If the host is
+named freerdp the created files are called freerdp.key and freerdp.crt.
+
+
+@MANPAGE_NAME@ -len 4096 -a sha384 -path /tmp -# 22 -m 144 -y 1 -format crt mycert
+
+The command above creates the file /tmp/mycert.pem containing a key and a
+certificate with a length of 4096. It will use sha384 as hash algorithm.
+The certificate has the serial number 22 and is valid for 12 years (144 months).
+.SH EXIT STATUS
+.TP
+.B 0
+Successful program execution.
+.TP
+.B 1
+Otherwise.
+
+.SH SEE ALSO
+
+.URL "https://msdn.microsoft.com/library/windows/desktop/aa386968.aspx" "MakeCert help page"
+
+.SH AUTHOR
+FreeRDP <team@freerdp.com>
diff --git a/winpr/tools/makecert/CMakeLists.txt b/winpr/tools/makecert/CMakeLists.txt
new file mode 100644
index 0000000..a41cccd
--- /dev/null
+++ b/winpr/tools/makecert/CMakeLists.txt
@@ -0,0 +1,49 @@
+# WinPR: Windows Portable Runtime
+# winpr-makecert cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(MODULE_NAME "winpr-makecert-tool")
+set(MODULE_PREFIX "WINPR_MAKECERT_TOOL")
+
+set(${MODULE_PREFIX}_SRCS makecert.c)
+
+if(OPENSSL_FOUND)
+ winpr_tools_include_directory_add(${OPENSSL_INCLUDE_DIR})
+endif()
+
+if(MBEDTLS_FOUND)
+ winpr_tools_include_directory_add(${MBEDTLS_INCLUDE_DIR})
+endif()
+
+
+winpr_tools_module_add(${${MODULE_PREFIX}_SRCS})
+
+if(OPENSSL_FOUND)
+ if(WIN32)
+ list(APPEND ${MODULE_PREFIX}_LIBS ${OPENSSL_LIBRARIES})
+ else()
+ # if ${OPENSSL_LIBRARIES} libssl and libcrypto is linked
+ # therefor explicitly link against libcrypto
+ list(APPEND ${MODULE_PREFIX}_LIBS ${OPENSSL_CRYPTO_LIBRARIES})
+ endif()
+endif()
+
+if(MBEDTLS_FOUND)
+ list(APPEND ${MODULE_PREFIX}_LIBS ${MBEDTLS_LIBRARIES})
+endif()
+
+
+winpr_tools_library_add(${${MODULE_PREFIX}_LIBS} winpr)
diff --git a/winpr/tools/makecert/makecert.c b/winpr/tools/makecert/makecert.c
new file mode 100644
index 0000000..85baeef
--- /dev/null
+++ b/winpr/tools/makecert/makecert.c
@@ -0,0 +1,1165 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * makecert replacement
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+
+#include <winpr/assert.h>
+#include <winpr/crt.h>
+#include <winpr/path.h>
+#include <winpr/file.h>
+#include <winpr/cmdline.h>
+#include <winpr/sysinfo.h>
+#include <winpr/crypto.h>
+
+#ifdef WITH_OPENSSL
+#include <openssl/crypto.h>
+#include <openssl/conf.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509v3.h>
+#include <openssl/bn.h>
+#endif
+
+#include <winpr/tools/makecert.h>
+
+struct S_MAKECERT_CONTEXT
+{
+ int argc;
+ char** argv;
+
+#ifdef WITH_OPENSSL
+ X509* x509;
+ EVP_PKEY* pkey;
+ PKCS12* pkcs12;
+#endif
+
+ BOOL live;
+ BOOL silent;
+
+ BOOL crtFormat;
+ BOOL pemFormat;
+ BOOL pfxFormat;
+
+ char* password;
+
+ char* output_file;
+ char* output_path;
+ char* default_name;
+ char* common_name;
+
+ int duration_years;
+ int duration_months;
+};
+
+static char* makecert_read_str(BIO* bio, size_t* pOffset)
+{
+ int status = -1;
+ size_t offset = 0;
+ size_t length = 0;
+ char* x509_str = NULL;
+
+ while (offset >= length)
+ {
+ size_t new_len = 0;
+ size_t readBytes = 0;
+ char* new_str = NULL;
+ new_len = length * 2;
+ if (new_len == 0)
+ new_len = 2048;
+
+ if (new_len > INT_MAX)
+ {
+ status = -1;
+ break;
+ }
+
+ new_str = (char*)realloc(x509_str, new_len);
+
+ if (!new_str)
+ {
+ status = -1;
+ break;
+ }
+
+ length = new_len;
+ x509_str = new_str;
+ ERR_clear_error();
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+ status = BIO_read_ex(bio, &x509_str[offset], length - offset, &readBytes);
+#else
+ status = BIO_read(bio, &x509_str[offset], length - offset);
+ readBytes = status;
+#endif
+ if (status <= 0)
+ break;
+
+ offset += (size_t)readBytes;
+ }
+
+ if (status < 0)
+ {
+ free(x509_str);
+ if (pOffset)
+ *pOffset = 0;
+ return NULL;
+ }
+
+ x509_str[offset] = '\0';
+ if (pOffset)
+ *pOffset = offset + 1;
+ return x509_str;
+}
+
+static int makecert_print_command_line_help(COMMAND_LINE_ARGUMENT_A* args, int argc, char** argv)
+{
+ char* str = NULL;
+ const COMMAND_LINE_ARGUMENT_A* arg = NULL;
+
+ if (!argv || (argc < 1))
+ return -1;
+
+ printf("Usage: %s [options] [output file]\n", argv[0]);
+ printf("\n");
+ arg = args;
+
+ do
+ {
+ if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
+ {
+ printf(" %s", "-");
+ printf("%-20s", arg->Name);
+ printf("\t%s\n", arg->Text);
+ }
+ else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
+ (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
+ {
+ printf(" %s", "-");
+
+ if (arg->Format)
+ {
+ size_t length = strlen(arg->Name) + strlen(arg->Format) + 2;
+ str = malloc(length + 1);
+
+ if (!str)
+ return -1;
+
+ sprintf_s(str, length + 1, "%s %s", arg->Name, arg->Format);
+ printf("%-20s", str);
+ free(str);
+ }
+ else
+ {
+ printf("%-20s", arg->Name);
+ }
+
+ printf("\t%s\n", arg->Text);
+ }
+ } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
+
+ return 1;
+}
+
+#ifdef WITH_OPENSSL
+static int x509_add_ext(X509* cert, int nid, char* value)
+{
+ X509V3_CTX ctx;
+ X509_EXTENSION* ext = NULL;
+
+ if (!cert || !value)
+ return 0;
+
+ X509V3_set_ctx_nodb(&ctx) X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
+ ext = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
+
+ if (!ext)
+ return 0;
+
+ X509_add_ext(cert, ext, -1);
+ X509_EXTENSION_free(ext);
+ return 1;
+}
+#endif
+
+static char* x509_name_parse(char* name, char* txt, size_t* length)
+{
+ char* p = NULL;
+ char* entry = NULL;
+
+ if (!name || !txt || !length)
+ return NULL;
+
+ p = strstr(name, txt);
+
+ if (!p)
+ return NULL;
+
+ entry = p + strlen(txt) + 1;
+ p = strchr(entry, '=');
+
+ if (!p)
+ *length = strlen(entry);
+ else
+ *length = (size_t)(p - entry);
+
+ return entry;
+}
+
+static char* x509_get_default_name(void)
+{
+ CHAR* computerName = NULL;
+ DWORD nSize = 0;
+
+ if (GetComputerNameExA(ComputerNamePhysicalDnsFullyQualified, NULL, &nSize) ||
+ GetLastError() != ERROR_MORE_DATA)
+ goto fallback;
+
+ computerName = (CHAR*)calloc(1, nSize);
+
+ if (!computerName)
+ goto fallback;
+
+ if (!GetComputerNameExA(ComputerNamePhysicalDnsFullyQualified, computerName, &nSize))
+ goto fallback;
+
+ return computerName;
+fallback:
+ free(computerName);
+
+ if (GetComputerNameExA(ComputerNamePhysicalNetBIOS, NULL, &nSize) ||
+ GetLastError() != ERROR_MORE_DATA)
+ return NULL;
+
+ computerName = (CHAR*)calloc(1, nSize);
+
+ if (!computerName)
+ return NULL;
+
+ if (!GetComputerNameExA(ComputerNamePhysicalNetBIOS, computerName, &nSize))
+ {
+ free(computerName);
+ return NULL;
+ }
+
+ return computerName;
+}
+
+static int command_line_pre_filter(MAKECERT_CONTEXT* context, int index, int argc, LPCSTR* argv)
+{
+ if (!context || !argv || (index < 0) || (argc < 0))
+ return -1;
+
+ if (index == (argc - 1))
+ {
+ if (argv[index][0] != '-')
+ {
+ context->output_file = _strdup(argv[index]);
+
+ if (!context->output_file)
+ return -1;
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int makecert_context_parse_arguments(MAKECERT_CONTEXT* context,
+ COMMAND_LINE_ARGUMENT_A* args, int argc, char** argv)
+{
+ int status = 0;
+ DWORD flags = 0;
+ const COMMAND_LINE_ARGUMENT_A* arg = NULL;
+
+ if (!context || !argv || (argc < 0))
+ return -1;
+
+ /**
+ * makecert -r -pe -n "CN=%COMPUTERNAME%" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr LocalMachine
+ * -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12
+ */
+ CommandLineClearArgumentsA(args);
+ flags = COMMAND_LINE_SEPARATOR_SPACE | COMMAND_LINE_SIGIL_DASH;
+ status =
+ CommandLineParseArgumentsA(argc, argv, args, flags, context,
+ (COMMAND_LINE_PRE_FILTER_FN_A)command_line_pre_filter, NULL);
+
+ if (status & COMMAND_LINE_STATUS_PRINT_HELP)
+ {
+ makecert_print_command_line_help(args, argc, argv);
+ return 0;
+ }
+
+ arg = args;
+ errno = 0;
+
+ do
+ {
+ if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
+ continue;
+
+ CommandLineSwitchStart(arg)
+ /* Basic Options */
+ CommandLineSwitchCase(arg, "silent")
+ {
+ context->silent = TRUE;
+ }
+ CommandLineSwitchCase(arg, "live")
+ {
+ context->live = TRUE;
+ }
+ CommandLineSwitchCase(arg, "format")
+ {
+ if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
+ continue;
+
+ if (strcmp(arg->Value, "crt") == 0)
+ {
+ context->crtFormat = TRUE;
+ context->pemFormat = FALSE;
+ context->pfxFormat = FALSE;
+ }
+ else if (strcmp(arg->Value, "pem") == 0)
+ {
+ context->crtFormat = FALSE;
+ context->pemFormat = TRUE;
+ context->pfxFormat = FALSE;
+ }
+ else if (strcmp(arg->Value, "pfx") == 0)
+ {
+ context->crtFormat = FALSE;
+ context->pemFormat = FALSE;
+ context->pfxFormat = TRUE;
+ }
+ else
+ return -1;
+ }
+ CommandLineSwitchCase(arg, "path")
+ {
+ if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
+ continue;
+
+ context->output_path = _strdup(arg->Value);
+
+ if (!context->output_path)
+ return -1;
+ }
+ CommandLineSwitchCase(arg, "p")
+ {
+ if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
+ continue;
+
+ context->password = _strdup(arg->Value);
+
+ if (!context->password)
+ return -1;
+ }
+ CommandLineSwitchCase(arg, "n")
+ {
+ if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
+ continue;
+
+ context->common_name = _strdup(arg->Value);
+
+ if (!context->common_name)
+ return -1;
+ }
+ CommandLineSwitchCase(arg, "y")
+ {
+ long val = 0;
+
+ if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
+ continue;
+
+ val = strtol(arg->Value, NULL, 0);
+
+ if ((errno != 0) || (val < 0) || (val > INT32_MAX))
+ return -1;
+
+ context->duration_years = (int)val;
+ }
+ CommandLineSwitchCase(arg, "m")
+ {
+ long val = 0;
+
+ if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
+ continue;
+
+ val = strtol(arg->Value, NULL, 0);
+
+ if ((errno != 0) || (val < 1) || (val > 12))
+ return -1;
+
+ context->duration_months = (int)val;
+ }
+ CommandLineSwitchDefault(arg)
+ {
+ }
+ CommandLineSwitchEnd(arg)
+ } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
+
+ return 1;
+}
+
+int makecert_context_set_output_file_name(MAKECERT_CONTEXT* context, const char* name)
+{
+ if (!context)
+ return -1;
+
+ free(context->output_file);
+ context->output_file = NULL;
+
+ if (name)
+ context->output_file = _strdup(name);
+
+ if (!context->output_file)
+ return -1;
+
+ return 1;
+}
+
+int makecert_context_output_certificate_file(MAKECERT_CONTEXT* context, const char* path)
+{
+#ifdef WITH_OPENSSL
+ FILE* fp = NULL;
+ int status = 0;
+ size_t length = 0;
+ size_t offset = 0;
+ char* filename = NULL;
+ char* fullpath = NULL;
+ char* ext = NULL;
+ int ret = -1;
+ BIO* bio = NULL;
+ char* x509_str = NULL;
+
+ if (!context || !path)
+ return -1;
+
+ if (!context->output_file)
+ {
+ context->output_file = _strdup(context->default_name);
+
+ if (!context->output_file)
+ return -1;
+ }
+
+ /*
+ * Output Certificate File
+ */
+ length = strlen(context->output_file);
+ filename = malloc(length + 8);
+
+ if (!filename)
+ return -1;
+
+ if (context->crtFormat)
+ ext = "crt";
+ else if (context->pemFormat)
+ ext = "pem";
+ else if (context->pfxFormat)
+ ext = "pfx";
+ else
+ goto out_fail;
+
+ sprintf_s(filename, length + 8, "%s.%s", context->output_file, ext);
+
+ if (path)
+ fullpath = GetCombinedPath(path, filename);
+ else
+ fullpath = _strdup(filename);
+
+ if (!fullpath)
+ goto out_fail;
+
+ fp = winpr_fopen(fullpath, "w+");
+
+ if (fp)
+ {
+ if (context->pfxFormat)
+ {
+ if (!context->password)
+ {
+ context->password = _strdup("password");
+
+ if (!context->password)
+ goto out_fail;
+
+ printf("Using default export password \"password\"\n");
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ OpenSSL_add_all_algorithms();
+ OpenSSL_add_all_ciphers();
+ OpenSSL_add_all_digests();
+#else
+ OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
+ OPENSSL_INIT_LOAD_CONFIG,
+ NULL);
+#endif
+ context->pkcs12 = PKCS12_create(context->password, context->default_name, context->pkey,
+ context->x509, NULL, 0, 0, 0, 0, 0);
+
+ if (!context->pkcs12)
+ goto out_fail;
+
+ bio = BIO_new(BIO_s_mem());
+
+ if (!bio)
+ goto out_fail;
+
+ status = i2d_PKCS12_bio(bio, context->pkcs12);
+
+ if (status != 1)
+ goto out_fail;
+
+ x509_str = makecert_read_str(bio, &offset);
+
+ if (!x509_str)
+ goto out_fail;
+
+ length = offset;
+
+ if (fwrite((void*)x509_str, length, 1, fp) != 1)
+ goto out_fail;
+ }
+ else
+ {
+ bio = BIO_new(BIO_s_mem());
+
+ if (!bio)
+ goto out_fail;
+
+ if (!PEM_write_bio_X509(bio, context->x509))
+ goto out_fail;
+
+ x509_str = makecert_read_str(bio, &offset);
+
+ if (!x509_str)
+ goto out_fail;
+
+ length = offset;
+
+ if (fwrite(x509_str, length, 1, fp) != 1)
+ goto out_fail;
+
+ free(x509_str);
+ x509_str = NULL;
+ BIO_free_all(bio);
+ bio = NULL;
+
+ if (context->pemFormat)
+ {
+ bio = BIO_new(BIO_s_mem());
+
+ if (!bio)
+ goto out_fail;
+
+ status = PEM_write_bio_PrivateKey(bio, context->pkey, NULL, NULL, 0, NULL, NULL);
+
+ if (status < 0)
+ goto out_fail;
+
+ x509_str = makecert_read_str(bio, &offset);
+ if (!x509_str)
+ goto out_fail;
+
+ length = offset;
+
+ if (fwrite(x509_str, length, 1, fp) != 1)
+ goto out_fail;
+ }
+ }
+ }
+
+ ret = 1;
+out_fail:
+ BIO_free_all(bio);
+
+ if (fp)
+ fclose(fp);
+
+ free(x509_str);
+ free(filename);
+ free(fullpath);
+ return ret;
+#else
+ WLog_ERR(TAG, "%s only supported with OpenSSL", __func__);
+ return -1;
+#endif
+}
+
+int makecert_context_output_private_key_file(MAKECERT_CONTEXT* context, const char* path)
+{
+#ifdef WITH_OPENSSL
+ FILE* fp = NULL;
+ size_t length = 0;
+ size_t offset = 0;
+ char* filename = NULL;
+ char* fullpath = NULL;
+ int ret = -1;
+ BIO* bio = NULL;
+ char* x509_str = NULL;
+
+ if (!context->crtFormat)
+ return 1;
+
+ if (!context->output_file)
+ {
+ context->output_file = _strdup(context->default_name);
+
+ if (!context->output_file)
+ return -1;
+ }
+
+ /**
+ * Output Private Key File
+ */
+ length = strlen(context->output_file);
+ filename = malloc(length + 8);
+
+ if (!filename)
+ return -1;
+
+ sprintf_s(filename, length + 8, "%s.key", context->output_file);
+
+ if (path)
+ fullpath = GetCombinedPath(path, filename);
+ else
+ fullpath = _strdup(filename);
+
+ if (!fullpath)
+ goto out_fail;
+
+ fp = winpr_fopen(fullpath, "w+");
+
+ if (!fp)
+ goto out_fail;
+
+ bio = BIO_new(BIO_s_mem());
+
+ if (!bio)
+ goto out_fail;
+
+ if (!PEM_write_bio_PrivateKey(bio, context->pkey, NULL, NULL, 0, NULL, NULL))
+ goto out_fail;
+
+ x509_str = makecert_read_str(bio, &offset);
+
+ if (!x509_str)
+ goto out_fail;
+
+ length = offset;
+
+ if (fwrite((void*)x509_str, length, 1, fp) != 1)
+ goto out_fail;
+
+ ret = 1;
+out_fail:
+
+ if (fp)
+ fclose(fp);
+
+ BIO_free_all(bio);
+ free(x509_str);
+ free(filename);
+ free(fullpath);
+ return ret;
+#else
+ WLog_ERR(TAG, "%s only supported with OpenSSL", __func__);
+ return -1;
+#endif
+}
+
+#ifdef WITH_OPENSSL
+static BOOL makecert_create_rsa(EVP_PKEY** ppkey, size_t key_length)
+{
+ BOOL rc = FALSE;
+
+ WINPR_ASSERT(ppkey);
+
+#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
+ RSA* rsa = NULL;
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
+ rsa = RSA_generate_key(key_length, RSA_F4, NULL, NULL);
+#else
+ {
+ BIGNUM* bn = BN_secure_new();
+
+ if (!bn)
+ return FALSE;
+
+ rsa = RSA_new();
+
+ if (!rsa)
+ {
+ BN_clear_free(bn);
+ return FALSE;
+ }
+
+ BN_set_word(bn, RSA_F4);
+ const int res = RSA_generate_key_ex(rsa, key_length, bn, NULL);
+ BN_clear_free(bn);
+
+ if (res != 1)
+ return FALSE;
+ }
+#endif
+
+ if (!EVP_PKEY_assign_RSA(*ppkey, rsa))
+ {
+ RSA_free(rsa);
+ return FALSE;
+ }
+ rc = TRUE;
+#else
+ EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
+ if (!pctx)
+ return FALSE;
+
+ if (EVP_PKEY_keygen_init(pctx) != 1)
+ goto fail;
+
+ WINPR_ASSERT(key_length <= UINT_MAX);
+ unsigned int keylen = (unsigned int)key_length;
+ const OSSL_PARAM params[] = { OSSL_PARAM_construct_uint("bits", &keylen),
+ OSSL_PARAM_construct_end() };
+ if (EVP_PKEY_CTX_set_params(pctx, params) != 1)
+ goto fail;
+
+ if (EVP_PKEY_generate(pctx, ppkey) != 1)
+ goto fail;
+
+ rc = TRUE;
+fail:
+ EVP_PKEY_CTX_free(pctx);
+#endif
+ return rc;
+}
+#endif
+
+int makecert_context_process(MAKECERT_CONTEXT* context, int argc, char** argv)
+{
+ COMMAND_LINE_ARGUMENT_A args[] = {
+ /* Custom Options */
+
+ { "rdp", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
+ "Unsupported - Generate certificate with required options for RDP usage." },
+ { "silent", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
+ "Silently generate certificate without verbose output." },
+ { "live", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
+ "Generate certificate live in memory when used as a library." },
+ { "format", COMMAND_LINE_VALUE_REQUIRED, "<crt|pem|pfx>", NULL, NULL, -1, NULL,
+ "Specify certificate file format" },
+ { "path", COMMAND_LINE_VALUE_REQUIRED, "<path>", NULL, NULL, -1, NULL,
+ "Specify certificate file output path" },
+ { "p", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL,
+ "Specify certificate export password" },
+
+ /* Basic Options */
+
+ { "n", COMMAND_LINE_VALUE_REQUIRED, "<name>", NULL, NULL, -1, NULL,
+ "Specifies the subject's certificate name. This name must conform to the X.500 standard. "
+ "The simplest method is to specify the name in double quotes, preceded by CN=; for "
+ "example, "
+ "-n \"CN=myName\"." },
+ { "pe", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
+ "Unsupported - Marks the generated private key as exportable. This allows the private "
+ "key to "
+ "be included in the certificate." },
+ { "sk", COMMAND_LINE_VALUE_REQUIRED, "<keyname>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the subject's key container location, which contains the "
+ "private "
+ "key. "
+ "If a key container does not exist, it will be created." },
+ { "sr", COMMAND_LINE_VALUE_REQUIRED, "<location>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the subject's certificate store location. location can be "
+ "either "
+ "currentuser (the default) or localmachine." },
+ { "ss", COMMAND_LINE_VALUE_REQUIRED, "<store>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the subject's certificate store name that stores the output "
+ "certificate." },
+ { "#", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
+ "Specifies a serial number from 1 to 2,147,483,647. The default is a unique value "
+ "generated "
+ "by Makecert.exe." },
+ { "$", COMMAND_LINE_VALUE_REQUIRED, "<authority>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the signing authority of the certificate, which must be set to "
+ "either commercial "
+ "(for certificates used by commercial software publishers) or individual (for "
+ "certificates "
+ "used by individual software publishers)." },
+
+ /* Extended Options */
+
+ { "a", COMMAND_LINE_VALUE_REQUIRED, "<algorithm>", NULL, NULL, -1, NULL,
+ "Specifies the signature algorithm. algorithm must be md5, sha1, sha256 (the default), "
+ "sha384, or sha512." },
+ { "b", COMMAND_LINE_VALUE_REQUIRED, "<mm/dd/yyyy>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the start of the validity period. Defaults to the current "
+ "date." },
+ { "crl", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
+ "Unsupported - Generates a certificate relocation list (CRL) instead of a certificate." },
+ { "cy", COMMAND_LINE_VALUE_REQUIRED, "<certType>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the certificate type. Valid values are end for end-entity and "
+ "authority for certification authority." },
+ { "e", COMMAND_LINE_VALUE_REQUIRED, "<mm/dd/yyyy>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the end of the validity period. Defaults to 12/31/2039 11:59:59 "
+ "GMT." },
+ { "eku", COMMAND_LINE_VALUE_REQUIRED, "<oid[,oid…]>", NULL, NULL, -1, NULL,
+ "Unsupported - Inserts a list of comma-separated, enhanced key usage object identifiers "
+ "(OIDs) into the certificate." },
+ { "h", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the maximum height of the tree below this certificate." },
+ { "ic", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the issuer's certificate file." },
+ { "ik", COMMAND_LINE_VALUE_REQUIRED, "<keyName>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the issuer's key container name." },
+ { "iky", COMMAND_LINE_VALUE_REQUIRED, "<keyType>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the issuer's key type, which must be one of the following: "
+ "signature (which indicates that the key is used for a digital signature), "
+ "exchange (which indicates that the key is used for key encryption and key exchange), "
+ "or an integer that represents a provider type. "
+ "By default, you can pass 1 for an exchange key or 2 for a signature key." },
+ { "in", COMMAND_LINE_VALUE_REQUIRED, "<name>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the issuer's certificate common name." },
+ { "ip", COMMAND_LINE_VALUE_REQUIRED, "<provider>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the issuer's CryptoAPI provider name. For information about the "
+ "CryptoAPI provider name, see the –sp option." },
+ { "ir", COMMAND_LINE_VALUE_REQUIRED, "<location>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the location of the issuer's certificate store. location can be "
+ "either currentuser (the default) or localmachine." },
+ { "is", COMMAND_LINE_VALUE_REQUIRED, "<store>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the issuer's certificate store name." },
+ { "iv", COMMAND_LINE_VALUE_REQUIRED, "<pvkFile>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the issuer's .pvk private key file." },
+ { "iy", COMMAND_LINE_VALUE_REQUIRED, "<type>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the issuer's CryptoAPI provider type. For information about the "
+ "CryptoAPI provider type, see the –sy option." },
+ { "l", COMMAND_LINE_VALUE_REQUIRED, "<link>", NULL, NULL, -1, NULL,
+ "Unsupported - Links to policy information (for example, to a URL)." },
+ { "len", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
+ "Specifies the generated key length, in bits." },
+ { "m", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
+ "Specifies the duration, in months, of the certificate validity period." },
+ { "y", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
+ "Specifies the duration, in years, of the certificate validity period." },
+ { "nscp", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
+ "Unsupported - Includes the Netscape client-authorization extension." },
+ { "r", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
+ "Unsupported - Creates a self-signed certificate." },
+ { "sc", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the subject's certificate file." },
+ { "sky", COMMAND_LINE_VALUE_REQUIRED, "<keyType>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the subject's key type, which must be one of the following: "
+ "signature (which indicates that the key is used for a digital signature), "
+ "exchange (which indicates that the key is used for key encryption and key exchange), "
+ "or an integer that represents a provider type. "
+ "By default, you can pass 1 for an exchange key or 2 for a signature key." },
+ { "sp", COMMAND_LINE_VALUE_REQUIRED, "<provider>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the subject's CryptoAPI provider name, which must be defined in "
+ "the "
+ "registry subkeys of "
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider. If both –sp "
+ "and "
+ "–sy are present, "
+ "the type of the CryptoAPI provider must correspond to the Type value of the provider's "
+ "subkey." },
+ { "sv", COMMAND_LINE_VALUE_REQUIRED, "<pvkFile>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the subject's .pvk private key file. The file is created if "
+ "none "
+ "exists." },
+ { "sy", COMMAND_LINE_VALUE_REQUIRED, "<type>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the subject's CryptoAPI provider type, which must be defined in "
+ "the "
+ "registry subkeys of "
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider Types. If "
+ "both "
+ "–sy and –sp are present, "
+ "the name of the CryptoAPI provider must correspond to the Name value of the provider "
+ "type "
+ "subkey." },
+ { "tbs", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
+ "Unsupported - Specifies the certificate or CRL file to be signed." },
+
+ /* Help */
+
+ { "?", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "help",
+ "print help" },
+ { "!", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "help-ext",
+ "print extended help" },
+ { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
+ };
+#ifdef WITH_OPENSSL
+ size_t length = 0;
+ char* entry = NULL;
+ int key_length = 0;
+ long serial = 0;
+ X509_NAME* name = NULL;
+ const EVP_MD* md = NULL;
+ const COMMAND_LINE_ARGUMENT_A* arg = NULL;
+ int ret = 0;
+ ret = makecert_context_parse_arguments(context, args, argc, argv);
+
+ if (ret < 1)
+ {
+ return ret;
+ }
+
+ if (!context->default_name && !context->common_name)
+ {
+ context->default_name = x509_get_default_name();
+
+ if (!context->default_name)
+ return -1;
+ }
+ else
+ {
+ context->default_name = _strdup(context->common_name);
+
+ if (!context->default_name)
+ return -1;
+ }
+
+ if (!context->common_name)
+ {
+ context->common_name = _strdup(context->default_name);
+
+ if (!context->common_name)
+ return -1;
+ }
+
+ if (!context->pkey)
+ context->pkey = EVP_PKEY_new();
+
+ if (!context->pkey)
+ return -1;
+
+ if (!context->x509)
+ context->x509 = X509_new();
+
+ if (!context->x509)
+ return -1;
+
+ key_length = 2048;
+ arg = CommandLineFindArgumentA(args, "len");
+
+ if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
+ {
+ unsigned long val = strtoul(arg->Value, NULL, 0);
+
+ if ((errno != 0) || (val > INT_MAX))
+ return -1;
+ key_length = (int)val;
+ }
+
+ if (!makecert_create_rsa(&context->pkey, key_length))
+ return -1;
+
+ X509_set_version(context->x509, 2);
+ arg = CommandLineFindArgumentA(args, "#");
+
+ if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
+ {
+ serial = strtol(arg->Value, NULL, 0);
+
+ if (errno != 0)
+ return -1;
+ }
+ else
+ serial = (long)GetTickCount64();
+
+ ASN1_INTEGER_set(X509_get_serialNumber(context->x509), serial);
+ {
+ ASN1_TIME* before = NULL;
+ ASN1_TIME* after = NULL;
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
+ before = X509_get_notBefore(context->x509);
+ after = X509_get_notAfter(context->x509);
+#else
+ before = X509_getm_notBefore(context->x509);
+ after = X509_getm_notAfter(context->x509);
+#endif
+ X509_gmtime_adj(before, 0);
+
+ if (context->duration_months)
+ X509_gmtime_adj(after, (long)(60 * 60 * 24 * 31 * context->duration_months));
+ else if (context->duration_years)
+ X509_gmtime_adj(after, (long)(60 * 60 * 24 * 365 * context->duration_years));
+ }
+ X509_set_pubkey(context->x509, context->pkey);
+ name = X509_get_subject_name(context->x509);
+ arg = CommandLineFindArgumentA(args, "n");
+
+ if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
+ {
+ entry = x509_name_parse(arg->Value, "C", &length);
+
+ if (entry)
+ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_UTF8, (const unsigned char*)entry,
+ (int)length, -1, 0);
+
+ entry = x509_name_parse(arg->Value, "ST", &length);
+
+ if (entry)
+ X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_UTF8, (const unsigned char*)entry,
+ (int)length, -1, 0);
+
+ entry = x509_name_parse(arg->Value, "L", &length);
+
+ if (entry)
+ X509_NAME_add_entry_by_txt(name, "L", MBSTRING_UTF8, (const unsigned char*)entry,
+ (int)length, -1, 0);
+
+ entry = x509_name_parse(arg->Value, "O", &length);
+
+ if (entry)
+ X509_NAME_add_entry_by_txt(name, "O", MBSTRING_UTF8, (const unsigned char*)entry,
+ (int)length, -1, 0);
+
+ entry = x509_name_parse(arg->Value, "OU", &length);
+
+ if (entry)
+ X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_UTF8, (const unsigned char*)entry,
+ (int)length, -1, 0);
+
+ entry = context->common_name;
+ length = strlen(entry);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, (const unsigned char*)entry,
+ (int)length, -1, 0);
+ }
+ else
+ {
+ entry = context->common_name;
+ length = strlen(entry);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, (const unsigned char*)entry,
+ (int)length, -1, 0);
+ }
+
+ X509_set_issuer_name(context->x509, name);
+ x509_add_ext(context->x509, NID_ext_key_usage, "serverAuth");
+ arg = CommandLineFindArgumentA(args, "a");
+ md = EVP_sha256();
+
+ if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
+ {
+ md = EVP_get_digestbyname(arg->Value);
+ if (!md)
+ return -1;
+ }
+
+ if (!X509_sign(context->x509, context->pkey, md))
+ return -1;
+
+ /**
+ * Print certificate
+ */
+
+ if (!context->silent)
+ {
+ BIO* bio = NULL;
+ int status = 0;
+ char* x509_str = NULL;
+ bio = BIO_new(BIO_s_mem());
+
+ if (!bio)
+ return -1;
+
+ status = X509_print(bio, context->x509);
+
+ if (status < 0)
+ {
+ BIO_free_all(bio);
+ return -1;
+ }
+
+ x509_str = makecert_read_str(bio, NULL);
+ if (!x509_str)
+ {
+ BIO_free_all(bio);
+ return -1;
+ }
+
+ printf("%s", x509_str);
+ free(x509_str);
+ BIO_free_all(bio);
+ }
+
+ /**
+ * Output certificate and private key to files
+ */
+
+ if (!context->live)
+ {
+ if (!winpr_PathFileExists(context->output_path))
+ {
+ if (!CreateDirectoryA(context->output_path, NULL))
+ return -1;
+ }
+
+ if (makecert_context_output_certificate_file(context, context->output_path) != 1)
+ return -1;
+
+ if (context->crtFormat)
+ {
+ if (makecert_context_output_private_key_file(context, context->output_path) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+#else
+ WLog_ERR(TAG, "%s only supported with OpenSSL", __func__);
+ return -1;
+#endif
+}
+
+MAKECERT_CONTEXT* makecert_context_new(void)
+{
+ MAKECERT_CONTEXT* context = (MAKECERT_CONTEXT*)calloc(1, sizeof(MAKECERT_CONTEXT));
+
+ if (context)
+ {
+ context->crtFormat = TRUE;
+ context->duration_years = 1;
+ }
+
+ return context;
+}
+
+void makecert_context_free(MAKECERT_CONTEXT* context)
+{
+ if (context)
+ {
+ free(context->password);
+ free(context->default_name);
+ free(context->common_name);
+ free(context->output_file);
+ free(context->output_path);
+#ifdef WITH_OPENSSL
+ X509_free(context->x509);
+ EVP_PKEY_free(context->pkey);
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
+ CRYPTO_cleanup_all_ex_data();
+#endif
+#endif
+ free(context);
+ }
+}
diff --git a/winpr/tools/winpr-tools.pc.in b/winpr/tools/winpr-tools.pc.in
new file mode 100644
index 0000000..6898253
--- /dev/null
+++ b/winpr/tools/winpr-tools.pc.in
@@ -0,0 +1,15 @@
+prefix=@PKG_CONFIG_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@WINPR_INCLUDE_DIR@
+libs=-lwinpr-tools@WINPR_TOOLS_API_VERSION@
+
+Name: WinPR
+Description: WinPR: Windows Portable Runtime
+URL: http://www.freerdp.com/
+Version: @WINPR_TOOLS_VERSION@
+Requires:
+Requires.private: winpr@WINPR_VERSION_MAJOR@ libssl
+Libs: -L${libdir} ${libs}
+Libs.private: -lcrypto
+Cflags: -I${includedir}