Coming to the company on Monday, I found my usual freerdp couldn't connect to company services. I'll introduce my investigation and solution — not necessarily applicable to all situations, just sharing here.
I mainly used the xfreerdp tool, already introduced in an earlier blog.
Coming to the company on Monday, I found my usual freerdp couldn’t connect to company services. I’ll introduce my investigation and solution — not necessarily applicable to all situations, just sharing here.
Errors Encountered in Use
The error on linux
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
[INFO][com.freerdp.core] - freerdp_connect:freerdp_set_last_error_ex resetting error state [INFO][com.freerdp.client.common.cmdline] - loading channelEx rdpdr [INFO][com.freerdp.client.common.cmdline] - loading channelEx rdpsnd [INFO][com.freerdp.client.common.cmdline] - loading channelEx cliprdr [INFO][com.freerdp.client.common.cmdline] - loading channelEx drdynvc [INFO][com.freerdp.primitives] - primitives autodetect, using optimized 1f40] Failed to initialise VAAPI connection: -1 (unknown libva error). [ERROR][com.freerdp.codec] - Could not initialize hardware decoder, falling back to software: Input/output error [INFO][com.freerdp.core] - freerdp_tcp_is_hostname_resolvable:freerdp_set_last_error_ex resetting error state [INFO][com.freerdp.core] - freerdp_tcp_connect:freerdp_set_last_error_ex resetting error state [WARN][com.freerdp.crypto] - Certificate verification failure 'unable to get local issuer certificate (20)' at stack position 0 [WARN][com.freerdp.crypto] - CN = XXXXXX.xxx.xxx [ERROR][com.freerdp.core.nla] - SPNEGO failed with NTSTATUS: 0xC0000070 [ERROR][com.freerdp.core] - nla_recv_pdu:freerdp_set_last_error_ex ERRCONNECT_AUTHENTICATION_FAILED [0x00020009] [ERROR][com.freerdp.core.rdp] - rdp_recv_callback: CONNECTION_STATE_NLA - nla_recv_pdu() fail [ERROR][com.freerdp.core.transport] - transport_check_fds: transport->ReceiveCallback() - -1
The useful error data is this line: ERRCONNECT_AUTHENTICATION_FAILED [0x00020009] — the remote computer returned an error code. I wanted to ask our IT department what on earth was going on,
but I certainly couldn’t ask using Linux error codes — they don’t use Linux either. So I also tested with mstsc in my own virtual machine.
The mstsc error on windows
Problem Solved
After asking our IT colleagues, I found the company’s domain network had added a restriction: only users whose machine name equals their account may log in. For example, I’m fengxxx, so I must also change my computer’s name to fengxxx.
I never imagined there could be such a layer of restriction. Because of this inexplicable convention, remote connection becomes less convenient while providing no substantive help to security.
The Solution on Linux
Knowing the restriction was made via machine name, I first changed the system-wide machine name; connecting again indeed worked.
1
sudo hostnamectl set-hostname fengxxx
After much thought, I felt changing my laptop’s machine name just for one rdp was completely unnecessary, and wondered whether there was some way to hack it.
First I traced where FreeRdp gets the machine name:
if (!lpnSize) { SetLastError(ERROR_BAD_ARGUMENTS); return FALSE; }
if (gethostname(hostname, sizeof(hostname)) == -1) return FALSE; }
I found it uses the gethostname function. I remembered Linux can use LD_PRELOAD to hack
system functions. I studied the libkeepalive library, which can replace gethostname. Here is my code:
// https://github.com/corvofeng/libgethostname // The concrete strategy: read a string from the environment variable `FAKEHOST` and return it when the program calls gethostname // // gcc -fPIC -c -o libgethostname.o libgethostname.c // gcc -shared -Wl,-soname,libgethostname.so -o libgethostname.so libgethostname.o -ldl // then you can use it like this // FAKEHOST=fengxxx LD_PRELOAD="./libgethostname.so" hostname #ifndef RTLD_NEXT # define _GNU_SOURCE #endif #include<stdlib.h> #include<stdio.h> #include<string.h> #include<dlfcn.h> #include<errno.h> #include<unistd.h>
intgethostname(char *__name, size_t __len);
intmin(int x, int y) { return (x < y) ? x : y; }
intgethostname(char *__name, size_t __len) { int (*libc_gethostname)(char *__name, size_t __len);
From the perspective of rdp security, I think restricting by machine name is quite unreasonable — after all, the machine name can be easily forged.
The code above is a good example. If some domain administrator reads this blog, comments are welcome.