diff --git a/client/test/CMakeLists.txt b/client/test/CMakeLists.txt index 4a4f6fca7..f07f72dc3 100644 --- a/client/test/CMakeLists.txt +++ b/client/test/CMakeLists.txt @@ -24,3 +24,4 @@ target_link_libraries(freerdp-test freerdp-core) target_link_libraries(freerdp-test freerdp-gdi) target_link_libraries(freerdp-test freerdp-utils) target_link_libraries(freerdp-test freerdp-channels ${CMAKE_DL_LIBS}) + diff --git a/include/winpr/ntlm.h b/include/winpr/ntlm.h new file mode 100644 index 000000000..6e8759ceb --- /dev/null +++ b/include/winpr/ntlm.h @@ -0,0 +1,46 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Utils + * + * Copyright 2012 Marc-Andre Moreau + * + * 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. + */ + +#ifndef WINPR_UTILS_NTLM_H +#define WINPR_UTILS_NTLM_H + +#include +#include +#include +#include +#include + +WINPR_API BYTE* NTOWFv1W(LPWSTR Password, UINT32 PasswordLength, BYTE* NtHash); +WINPR_API BYTE* NTOWFv1A(LPSTR Password, UINT32 PasswordLength, BYTE* NtHash); + +WINPR_API BYTE* NTOWFv2W(LPWSTR Password, UINT32 PasswordLength, LPWSTR User, + UINT32 UserLength, LPWSTR Domain, UINT32 DomainLength, BYTE* NtHash); +WINPR_API BYTE* NTOWFv2A(LPSTR Password, UINT32 PasswordLength, LPSTR User, + UINT32 UserLength, LPSTR Domain, UINT32 DomainLength, BYTE* NtHash); + +#ifdef UNICODE +#define NTOWFv1 NTOWFv1W +#define NTOWFv2 NTOWFv2W +#else +#define NTOWFv1 NTOWFv1A +#define NTOWFv2 NTOWFv2W +#endif + +#endif /* WINPR_UTILS_NTLM_H */ + diff --git a/winpr/CMakeLists.txt b/winpr/CMakeLists.txt index 77611e022..1b4e1b5f9 100644 --- a/winpr/CMakeLists.txt +++ b/winpr/CMakeLists.txt @@ -27,3 +27,5 @@ add_subdirectory(rpc) add_subdirectory(sspi) add_subdirectory(registry) +add_subdirectory(tools) + diff --git a/winpr/sspi/NTLM/ntlm_compute.c b/winpr/sspi/NTLM/ntlm_compute.c index dc4f27de2..e2ce6e385 100644 --- a/winpr/sspi/NTLM/ntlm_compute.c +++ b/winpr/sspi/NTLM/ntlm_compute.c @@ -21,6 +21,7 @@ #include "../sspi.h" #include +#include #include #include "ntlm_compute.h" @@ -159,21 +160,6 @@ void ntlm_generate_timestamp(NTLM_CONTEXT* context) } } -void ntlm_compute_ntlm_hash(UINT16* password, UINT32 length, char* hash) -{ - /* NTLMv1("password") = 8846F7EAEE8FB117AD06BDD830B7586C */ - - MD4_CTX md4_ctx; - - /* Password needs to be in unicode */ - - /* Apply the MD4 digest algorithm on the password in unicode, the result is the NTLM hash */ - - MD4_Init(&md4_ctx); - MD4_Update(&md4_ctx, password, length); - MD4_Final((void*) hash, &md4_ctx); -} - static void ascii_hex_string_to_binary(char* str, unsigned char* hex) { int i; @@ -278,36 +264,16 @@ void ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, char* hash) void ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, char* hash) { - char* p; - SecBuffer buffer; - char ntlm_hash[16]; - if (context->identity.PasswordLength > 0) { - /* First, compute the NTLMv1 hash of the password */ - ntlm_compute_ntlm_hash(context->identity.Password, context->identity.PasswordLength, ntlm_hash); - } - - sspi_SecBufferAlloc(&buffer, context->identity.UserLength + context->identity.DomainLength); - p = (char*) buffer.pvBuffer; - - /* Concatenate(Uppercase(username),domain)*/ - CopyMemory(p, context->identity.User, context->identity.UserLength); - CharUpperBuffW((LPWSTR) p, context->identity.UserLength / 2); - - CopyMemory(&p[context->identity.UserLength], context->identity.Domain, context->identity.DomainLength); - - if (context->identity.PasswordLength > 0) - { - /* Compute the HMAC-MD5 hash of the above value using the NTLMv1 hash as the key, the result is the NTLMv2 hash */ - HMAC(EVP_md5(), (void*) ntlm_hash, 16, buffer.pvBuffer, buffer.cbBuffer, (void*) hash, NULL); + NTOWFv2W((LPWSTR) context->identity.Password, context->identity.PasswordLength, + (LPWSTR) context->identity.User, context->identity.UserLength, + (LPWSTR) context->identity.Domain, context->identity.DomainLength, (BYTE*) hash); } else { ntlm_fetch_ntlm_v2_hash(context, hash); } - - sspi_SecBufferFree(&buffer); } void ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) diff --git a/winpr/tools/CMakeLists.txt b/winpr/tools/CMakeLists.txt new file mode 100644 index 000000000..8c782bcbc --- /dev/null +++ b/winpr/tools/CMakeLists.txt @@ -0,0 +1,21 @@ +# WinPR: Windows Portable Runtime +# winpr cmake build script +# +# Copyright 2011 O.S. Systems Software Ltda. +# Copyright 2011 Otavio Salvador +# Copyright 2011 Marc-Andre Moreau +# +# 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. + +add_subdirectory(hash) + diff --git a/winpr/tools/hash/CMakeLists.txt b/winpr/tools/hash/CMakeLists.txt new file mode 100644 index 000000000..b8a1fc46c --- /dev/null +++ b/winpr/tools/hash/CMakeLists.txt @@ -0,0 +1,24 @@ +# WinPR: Windows Portable Runtime +# winpr-hash cmake build script +# +# Copyright 2011 O.S. Systems Software Ltda. +# Copyright 2011 Otavio Salvador +# Copyright 2011 Marc-Andre Moreau +# +# 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. + +add_executable(winpr-hash + hash.c) + +target_link_libraries(winpr-hash winpr-utils) + diff --git a/winpr/tools/hash/hash.c b/winpr/tools/hash/hash.c new file mode 100644 index 000000000..ebe8f5952 --- /dev/null +++ b/winpr/tools/hash/hash.c @@ -0,0 +1,125 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Hashing Tool + * + * Copyright 2012 Marc-Andre Moreau + * + * 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 +#include +#include + +#include + +/** + * 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 + * + */ + +int main(int argc, char* argv[]) +{ + int index = 1; + BYTE NtHash[16]; + char* User = NULL; + UINT32 UserLength; + char* Domain = NULL; + UINT32 DomainLength; + char* Password = NULL; + UINT32 PasswordLength; + + while (index < argc) + { + if (strcmp("-d", argv[index]) == 0) + { + index++; + + if (index == argc) + { + printf("missing domain\n"); + exit(1); + } + + Domain = argv[index]; + } + else if (strcmp("-u", argv[index]) == 0) + { + index++; + + if (index == argc) + { + printf("missing username\n"); + exit(1); + } + + User = argv[index]; + } + else if (strcmp("-p", argv[index]) == 0) + { + index++; + + if (index == argc) + { + printf("missing password\n"); + exit(1); + } + + Password = argv[index]; + } + else if (strcmp("-h", argv[index]) == 0) + { + printf("winpr-hash: NTLM hashing tool\n"); + printf("Usage: winpr-hash -u -p [-d ]\n"); + exit(1); + } + + index++; + } + + if ((!User) || (!Password)) + { + printf("missing username or password\n"); + exit(1); + } + + UserLength = strlen(User); + PasswordLength = strlen(Password); + DomainLength = (Domain) ? strlen(Domain) : 0; + + NTOWFv2A(Password, PasswordLength, User, UserLength, Domain, DomainLength, NtHash); + + for (index = 0; index < 16; index++) + printf("%02x", NtHash[index]); + + printf("\n"); + + return 0; +} + diff --git a/winpr/tools/hash/winpr-hash b/winpr/tools/hash/winpr-hash new file mode 100755 index 000000000..7ee8850c2 Binary files /dev/null and b/winpr/tools/hash/winpr-hash differ diff --git a/winpr/utils/CMakeLists.txt b/winpr/utils/CMakeLists.txt index 33b1c427c..7363a123e 100644 --- a/winpr/utils/CMakeLists.txt +++ b/winpr/utils/CMakeLists.txt @@ -18,6 +18,7 @@ # limitations under the License. set(WINPR_UTILS_SRCS + ntlm.c print.c stream.c) @@ -29,5 +30,8 @@ if (NOT WIN32) target_link_libraries(winpr-utils winpr-crt) endif() +target_link_libraries(winpr-utils ${ZLIB_LIBRARIES}) +target_link_libraries(winpr-utils ${OPENSSL_LIBRARIES}) + install(TARGETS winpr-utils DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/winpr/utils/ntlm.c b/winpr/utils/ntlm.c new file mode 100644 index 000000000..be2851705 --- /dev/null +++ b/winpr/utils/ntlm.c @@ -0,0 +1,131 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Utils + * + * Copyright 2012 Marc-Andre Moreau + * + * 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Define NTOWFv1(Password, User, Domain) as + * MD4(UNICODE(Password)) + * EndDefine + */ + +BYTE* NTOWFv1W(LPWSTR Password, UINT32 PasswordLength, BYTE* NtHash) +{ + MD4_CTX md4_ctx; + + if (!Password) + return NULL; + + if (!NtHash) + NtHash = malloc(16); + + MD4_Init(&md4_ctx); + MD4_Update(&md4_ctx, Password, PasswordLength); + MD4_Final((void*) NtHash, &md4_ctx); + + return NtHash; +} + +BYTE* NTOWFv1A(LPSTR Password, UINT32 PasswordLength, BYTE* NtHash) +{ + LPWSTR PasswordW = NULL; + + PasswordW = (LPWSTR) malloc(PasswordLength * 2); + MultiByteToWideChar(CP_ACP, 0, Password, PasswordLength, PasswordW, PasswordLength); + + NtHash = NTOWFv1W(PasswordW, PasswordLength, NtHash); + + free(PasswordW); + + return NtHash; +} + +/** + * Define NTOWFv2(Password, User, Domain) as + * HMAC_MD5(MD4(UNICODE(Password)), + * UNICODE(ConcatenationOf(UpperCase(User), Domain))) + * EndDefine + */ + +BYTE* NTOWFv2W(LPWSTR Password, UINT32 PasswordLength, LPWSTR User, + UINT32 UserLength, LPWSTR Domain, UINT32 DomainLength, BYTE* NtHash) +{ + BYTE* buffer; + BYTE NtHashV1[16]; + + if ((!User) || (!Password)) + return NULL; + + if (!NtHash) + NtHash = (BYTE*) malloc(16); + + NTOWFv1W(Password, PasswordLength, NtHashV1); + + buffer = (BYTE*) malloc(UserLength + DomainLength); + + /* Concatenate(UpperCase(User), Domain) */ + + CopyMemory(buffer, User, UserLength); + CharUpperBuffW((LPWSTR) buffer, UserLength / 2); + CopyMemory(&buffer[UserLength], Domain, DomainLength); + + /* Compute the HMAC-MD5 hash of the above value using the NTLMv1 hash as the key, the result is the NTLMv2 hash */ + HMAC(EVP_md5(), (void*) NtHashV1, 16, buffer, UserLength + DomainLength, (void*) NtHash, NULL); + + free(buffer); + + return NtHash; +} + +BYTE* NTOWFv2A(LPSTR Password, UINT32 PasswordLength, LPSTR User, + UINT32 UserLength, LPSTR Domain, UINT32 DomainLength, BYTE* NtHash) +{ + LPWSTR UserW = NULL; + LPWSTR DomainW = NULL; + LPWSTR PasswordW = NULL; + + UserW = (LPWSTR) malloc(UserLength * 2); + DomainW = (LPWSTR) malloc(DomainLength * 2); + PasswordW = (LPWSTR) malloc(PasswordLength * 2); + + MultiByteToWideChar(CP_ACP, 0, User, UserLength, UserW, UserLength); + MultiByteToWideChar(CP_ACP, 0, Domain, DomainLength, DomainW, DomainLength); + MultiByteToWideChar(CP_ACP, 0, Password, PasswordLength, PasswordW, PasswordLength); + + NtHash = NTOWFv2W(PasswordW, PasswordLength * 2, UserW, UserLength * 2, DomainW, DomainLength * 2, NtHash); + + free(UserW); + free(DomainW); + free(PasswordW); + + return NtHash; +} +