pastebin - collaborative debugging tool
rovema.kpaste.net RSS


msnfs41client: nfs_mount: Do not pass raw IPv6 addresses with colons to the kernel
Posted by Anonymous on Thu 28th Sep 2023 13:52
raw | new post

  1. From 1d7a12676c246a3955f926e461ee62a33ef0756b Mon Sep 17 00:00:00 2001
  2. From: Roland Mainz <roland.mainz@nrubsig.org>
  3. Date: Thu, 28 Sep 2023 14:39:06 +0200
  4. Subject: [PATCH] nfs_mount: Do not pass raw IPv6 addresses with colons to the
  5.  kernel
  6.  
  7. nfs_mount.exe should not pass raw IPv6 addresses to the kernel, as
  8. ':' are invalid in UNC paths and cause weired errors.
  9.  
  10. Microsoft solved this problem by providing a transcription method to
  11. represent an IPv6 address in the form of a domain name that can be
  12. used in UNC paths, e.g. "[fe80::219:99ff:feae:73ce]" should be turned
  13. into "fe80--219-99ff-feae-73ce.ipv6-literal.net"
  14.  
  15. See https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_UNC_path_names
  16. for details.
  17.  
  18. The patch also updates the help message to explain the transcription
  19. for raw IPv6 addresses.
  20.  
  21. Reviewed-by: Joshuah Hurst <joshhurst@gmail.com>
  22. Reviewed-by: Cedric Blancher <cedric.blancher@gmail.com>
  23. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  24. ---
  25. daemon/nfs41_server.c |  19 +++++++
  26.  mount/mount.c         | 114 +++++++++++++++++++++++++++++++++++++-----
  27.  2 files changed, 120 insertions(+), 13 deletions(-)
  28.  
  29. diff --git a/daemon/nfs41_server.c b/daemon/nfs41_server.c
  30. index ef8f0ac..b4fdba2 100644
  31. --- a/daemon/nfs41_server.c
  32. +++ b/daemon/nfs41_server.c
  33. @@ -22,6 +22,7 @@
  34.  #include <Windows.h>
  35.  #include <strsafe.h>
  36.  #include <stdio.h>
  37. +#include <ctype.h>
  38.  
  39.  #include "wintirpc.h"
  40.  #include "rpc/rpc.h"
  41. @@ -283,12 +284,30 @@ int nfs41_server_resolve(
  42.      struct netconfig *nconf;
  43.      struct netbuf addr;
  44.      char *netid, *uaddr;
  45. +    const char *s;
  46.  
  47.      dprintf(SRVLVL, "--> nfs41_server_resolve(%s:%u)\n",
  48.          hostname, port);
  49.  
  50.      addrs->count = 0;
  51.  
  52. +    /*
  53. +     * Harden against garbage
  54. +     * (originally added to avoid ':' characters (for example from
  55. +     * raw IPv6 address strings), which are illegal in UNC paths)
  56. +     * ('_' is an extention as SystemV and Windows allow this in
  57. +     * /etc/hosts & /etc/inet/hosts)
  58. +     */
  59. +#define isvalidhostnamechar(c) \
  60. +       (isalnum(c) || ((c) == '.') || ((c) == '-') || ((c) == '_'))
  61. +    for (s=hostname ; *s != '\0' ; s++) {
  62. +        if (!isvalidhostnamechar(*s)) {
  63. +           eprintf("nfs41_server_resolve: Invalid hostname '%s'\n", hostname);
  64. +           status = ERROR_BAD_NET_NAME;
  65. +           goto out;
  66. +       }
  67. +    }
  68. +
  69.      StringCchPrintfA(service, 16, "%u", port);
  70.  
  71.      /* request a list of tcp addrs for the given hostname,port */
  72. diff --git a/mount/mount.c b/mount/mount.c
  73. index 48c7c88..f2d5650 100644
  74. --- a/mount/mount.c
  75. +++ b/mount/mount.c
  76. @@ -54,13 +54,13 @@ void PrintErrorMessage(
  77.  static VOID PrintUsage(LPTSTR pProcess)
  78.  {
  79.      _tprintf(TEXT("Usage: %s [options] <drive letter|*> <hostname>:<path>\n")
  80. -        TEXT("Options:\n")
  81. +        TEXT("* Options:\n")
  82.          TEXT("\t-h\thelp\n")
  83.          TEXT("\t-d\tunmount\n")
  84.          TEXT("\t-f\tforce unmount if the drive is in use\n")
  85.          TEXT("\t-p\tmake the mount persist over reboots\n")
  86.          TEXT("\t-o <comma-separated mount options>\n")
  87. -        TEXT("Mount options:\n")
  88. +        TEXT("* Mount options:\n")
  89.          TEXT("\tro\tmount as read-only\n")
  90.          TEXT("\tport=#\tTCP port to use (defaults to 2049)\n")
  91.          TEXT("\trsize=#\tread buffer size in bytes\n")
  92. @@ -68,7 +68,12 @@ static VOID PrintUsage(LPTSTR pProcess)
  93.          TEXT("\tsec=krb5:krb5i:krb5p\tspecify gss security flavor\n")
  94.          TEXT("\twritethru\tturns off rdbss caching for writes\n")
  95.          TEXT("\tnocache\tturns off rdbss caching\n")
  96. -        TEXT("\ttimeout=#\tspecify upcall timeout value in seconds (default 120s)\n"),
  97. +        TEXT("\ttimeout=#\tspecify upcall timeout value in seconds (default 120s)\n")
  98. +        TEXT("* Hostname:\n")
  99. +        TEXT("\tDNS name, or hostname in domain\n")
  100. +        TEXT("\tentry in C:\\Windows\\System32\\drivers\\etc\\hosts\n")
  101. +        TEXT("\tIPv4 address\n")
  102. +        TEXT("\tIPv6 address within '[', ']' (will be converted to *.ipv6-literal.net)\n"),
  103.          pProcess);
  104.  }
  105.  
  106. @@ -210,6 +215,7 @@ static void ConvertUnixSlashes(
  107.  static DWORD ParseRemoteName(
  108.      IN LPTSTR pRemoteName,
  109.      IN OUT PMOUNT_OPTION_LIST pOptions,
  110. +    OUT LPTSTR pParsedRemoteName,
  111.      OUT LPTSTR pConnectionName,
  112.      IN size_t cchConnectionLen)
  113.  {
  114. @@ -217,27 +223,31 @@ static DWORD ParseRemoteName(
  115.      LPTSTR pEnd;
  116.      int port = 0;
  117.      PFILE_FULL_EA_INFORMATION port_option_val;
  118. +    wchar_t remotename[MAX_PATH];
  119. +    wchar_t *premotename = remotename;
  120.      wchar_t srvname[MAX_PATH+1+32]; /* sizeof(hostname+'@'+integer) */
  121. -    
  122. +
  123. +    result = StringCchCopy(premotename, MAX_PATH, pRemoteName);
  124. +
  125.      /*
  126.       * gisburn: Fixme: Implement nfs://-URLS per RFC 2224 ("NFS URL
  127.       * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
  128.       * including port support (nfs://hostname@port/path/...)
  129.       */
  130. -    if (!wcsncmp(pRemoteName, TEXT("nfs://"), 6)) {
  131. +    if (!wcsncmp(premotename, TEXT("nfs://"), 6)) {
  132.          _ftprintf(stderr, TEXT("nfs://-URLs not supported yet.\n"));
  133.          result = ERROR_NOT_SUPPORTED;
  134.          goto out;
  135.      }
  136.  
  137. -    ConvertUnixSlashes(pRemoteName);
  138. +    ConvertUnixSlashes(premotename);
  139.  
  140.      /*
  141.       * Remote hostname should not contain a '@' since we use this
  142.       * to communicate the NFSv4 port number below
  143.       * Use $ nfs_mount.exe -o port=portnumber ... # instead
  144.       */
  145. -    if (_tcsrchr(pRemoteName, TEXT('@'))) {
  146. +    if (_tcsrchr(premotename, TEXT('@'))) {
  147.          _ftprintf(stderr, TEXT("Remote path should not contain '@', ")
  148.             TEXT("use -o port=tcpportnum.\n"));
  149.          result = ERROR_BAD_ARGUMENTS;
  150. @@ -259,7 +269,7 @@ static DWORD ParseRemoteName(
  151.      }
  152.  
  153.      /* fail if the server name doesn't end with :\ */
  154. -    pEnd = _tcsrchr(pRemoteName, TEXT(':'));
  155. +    pEnd = _tcsrchr(premotename, TEXT(':'));
  156.      if (pEnd == NULL || pEnd[1] != TEXT('\\')) {
  157.          _ftprintf(stderr, TEXT("Failed to parse the remote path. ")
  158.              TEXT("Expected 'hostname:\\path'.\n"));
  159. @@ -270,9 +280,84 @@ static DWORD ParseRemoteName(
  160.      ++pEnd;
  161.  
  162.      /*
  163. -     * ALWAYS add port number to hostname, so UNC paths use it too
  164. +     * Make sure that we do not pass raw IPv6 addresses to the kernel.
  165. +     *
  166. +     * UNC paths do not allow ':' characters, so passing a raw IPv6
  167. +     * address to the kernel is both illegal, and causes havoc.
  168. +     *
  169. +     * Microsoft solved this problem by providing a transcription
  170. +     * method to represent an IPv6 address in the form of a domain
  171. +     * name that can be used in UNC paths, e.g.
  172. +     * "[fe80::219:99ff:feae:73ce]" should be turned into
  173. +     * "fe80--219-99ff-feae-73ce.ipv6-literal.net"
  174. +     *
  175. +     * See https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_UNC_path_names
  176. +     * for details
  177. +     */
  178. +    if (premotename[0] == TEXT('[')) {
  179. +        size_t len = wcslen(premotename);
  180. +        size_t i;
  181. +        wchar_t c;
  182. +
  183. +        /* Check for minimum length and trailing ']' */
  184. +        if ((len < 4) || (premotename[len-1] != TEXT(']'))) {
  185. +            _ftprintf(stderr, TEXT("Failed to parse raw IPv6 address,")
  186. +               TEXT(" trailing ']' is missing, ")
  187. +               TEXT("or address string too short.\n"));
  188. +            result = ERROR_BAD_ARGUMENTS;
  189. +            goto out;
  190. +       }
  191. +
  192. +        /* Skip '[', stomp ']' */
  193. +        premotename[len-1] = TEXT('\0');
  194. +        premotename++;
  195. +        len -= 2;
  196. +
  197. +        /* Check whether this is a valid IPv6 address */
  198. +        for (i=0 ; i < len ; i++) {
  199. +            c = premotename[i];
  200. +            if (!(iswxdigit(c) || (c == TEXT(':')))) {
  201. +                _ftprintf(stderr, TEXT("Failed to parse raw IPv6 ")
  202. +                   TEXT("address, illegal character '%c' found.\n"),
  203. +                   c);
  204. +                result = ERROR_BAD_ARGUMENTS;
  205. +                goto out;
  206. +            }
  207. +        }
  208. +
  209. +       for (i = 0 ; i < len ; i++) {
  210. +           /* IPv6 separator */
  211. +            if (premotename[i] == TEXT(':'))
  212. +                premotename[i] = TEXT('-');
  213. +           /* zone index */
  214. +           else if (premotename[i] == TEXT('%'))
  215. +                premotename[i] = TEXT('s');
  216. +        }
  217. +
  218. +        /*
  219. +        * 1. Append .ipv6-literal.net to hostname
  220. +        * 2. ALWAYS add port number to hostname, so UNC paths use it
  221. +        *   too
  222. +        */
  223. +        (void)swprintf(srvname, sizeof(srvname),
  224. +           TEXT("%s.ipv6-literal.net@%d"), premotename, port);
  225. +    }
  226. +    else {
  227. +        /* ALWAYS add port number to hostname, so UNC paths use it too */
  228. +        (void)swprintf(srvname, sizeof(srvname), TEXT("%s@%d"),
  229. +           premotename, port);
  230. +    }
  231. +
  232. +    /*
  233. +     * Safeguard against ':' in UNC paths, e.g if we pass raw IPv6
  234. +     * address without ':', or just random garbage
  235.       */
  236. -    (void)swprintf(srvname, sizeof(srvname), TEXT("%s@%d"), pRemoteName, port);
  237. +    if (wcschr(srvname, TEXT(':'))) {
  238. +        _ftprintf(stderr,
  239. +           TEXT("Illegal ':' character hostname '%s'.\n"), srvname);
  240. +        result = ERROR_BAD_ARGUMENTS;
  241. +        goto out;
  242. +    }
  243.  
  244.      if (!InsertOption(TEXT("srvname"), srvname, pOptions) ||
  245.          !InsertOption(TEXT("mntpt"), *pEnd ? pEnd : TEXT("\\"), pOptions)) {
  246. @@ -303,6 +388,8 @@ static DWORD ParseRemoteName(
  247.      if (*pEnd)
  248.          result = StringCchCat(pConnectionName, cchConnectionLen, pEnd);
  249.  
  250. +    result = StringCchCopy(pParsedRemoteName, cchConnectionLen, srvname);
  251. +
  252.  out:
  253.      return result;
  254.  }
  255. @@ -315,11 +402,12 @@ static DWORD DoMount(
  256.  {
  257.      DWORD result = NO_ERROR;
  258.      TCHAR szExisting[MAX_PATH];
  259. +    TCHAR szParsedRemoteName[MAX_PATH];
  260.      TCHAR szRemoteName[MAX_PATH];
  261.      DWORD dwLength;
  262.  
  263.      *szRemoteName = TEXT('\0');
  264. -    result = ParseRemoteName(pRemoteName, pOptions, szRemoteName, MAX_PATH);
  265. +    result = ParseRemoteName(pRemoteName, pOptions, szParsedRemoteName, szRemoteName, MAX_PATH);
  266.      if (result)
  267.          goto out;
  268.  
  269. @@ -361,8 +449,8 @@ static DWORD DoMount(
  270.              szConnection, &ConnectSize, &ConnectResult);
  271.  
  272.          if (result == NO_ERROR)
  273. -            _tprintf(TEXT("Successfully mounted %s to drive %s\n"),
  274. -                pRemoteName, szConnection);
  275. +            _tprintf(TEXT("Successfully mounted '%s' to drive '%s'\n"),
  276. +                szParsedRemoteName, szConnection);
  277.          else
  278.              _ftprintf(stderr, TEXT("WNetUseConnection(%s, %s) ")
  279.                  TEXT("failed with error code %u.\n"),
  280. --
  281. 2.39.0

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at