dfs.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de>
  4. */
  5. #include "cifsproto.h"
  6. #include "cifs_debug.h"
  7. #include "dns_resolve.h"
  8. #include "fs_context.h"
  9. #include "dfs.h"
  10. /**
  11. * dfs_parse_target_referral - set fs context for dfs target referral
  12. *
  13. * @full_path: full path in UNC format.
  14. * @ref: dfs referral pointer.
  15. * @ctx: smb3 fs context pointer.
  16. *
  17. * Return zero if dfs referral was parsed correctly, otherwise non-zero.
  18. */
  19. int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
  20. struct smb3_fs_context *ctx)
  21. {
  22. int rc;
  23. const char *prepath = NULL;
  24. char *path;
  25. if (!full_path || !*full_path || !ref || !ctx)
  26. return -EINVAL;
  27. if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0))
  28. return -EINVAL;
  29. if (strlen(full_path) - ref->path_consumed) {
  30. prepath = full_path + ref->path_consumed;
  31. /* skip initial delimiter */
  32. if (*prepath == '/' || *prepath == '\\')
  33. prepath++;
  34. }
  35. path = cifs_build_devname(ref->node_name, prepath);
  36. if (IS_ERR(path))
  37. return PTR_ERR(path);
  38. rc = smb3_parse_devname(path, ctx);
  39. if (rc)
  40. goto out;
  41. rc = dns_resolve_server_name_to_ip(path, (struct sockaddr *)&ctx->dstaddr, NULL);
  42. out:
  43. kfree(path);
  44. return rc;
  45. }
  46. static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
  47. {
  48. struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
  49. int rc;
  50. ctx->leaf_fullpath = (char *)full_path;
  51. rc = cifs_mount_get_session(mnt_ctx);
  52. ctx->leaf_fullpath = NULL;
  53. return rc;
  54. }
  55. /*
  56. * Get an active reference of @ses so that next call to cifs_put_tcon() won't
  57. * release it as any new DFS referrals must go through its IPC tcon.
  58. */
  59. static void set_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
  60. {
  61. struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
  62. struct cifs_ses *ses = mnt_ctx->ses;
  63. if (ses) {
  64. spin_lock(&cifs_tcp_ses_lock);
  65. cifs_smb_ses_inc_refcount(ses);
  66. spin_unlock(&cifs_tcp_ses_lock);
  67. }
  68. ctx->dfs_root_ses = ses;
  69. }
  70. static inline int parse_dfs_target(struct smb3_fs_context *ctx,
  71. struct dfs_ref_walk *rw,
  72. struct dfs_info3_param *tgt)
  73. {
  74. int rc;
  75. const char *fpath = ref_walk_fpath(rw) + 1;
  76. rc = ref_walk_get_tgt(rw, tgt);
  77. if (!rc)
  78. rc = dfs_parse_target_referral(fpath, tgt, ctx);
  79. return rc;
  80. }
  81. static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx,
  82. struct dfs_info3_param *tgt,
  83. struct dfs_ref_walk *rw)
  84. {
  85. struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
  86. struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
  87. char *ref_path, *full_path;
  88. int rc;
  89. full_path = smb3_fs_context_fullpath(ctx, CIFS_DIR_SEP(cifs_sb));
  90. if (IS_ERR(full_path))
  91. return PTR_ERR(full_path);
  92. if (!tgt || (tgt->server_type == DFS_TYPE_LINK &&
  93. DFS_INTERLINK(tgt->flags)))
  94. ref_path = dfs_get_path(cifs_sb, ctx->UNC);
  95. else
  96. ref_path = dfs_get_path(cifs_sb, full_path);
  97. if (IS_ERR(ref_path)) {
  98. rc = PTR_ERR(ref_path);
  99. kfree(full_path);
  100. return rc;
  101. }
  102. ref_walk_path(rw) = ref_path;
  103. ref_walk_fpath(rw) = full_path;
  104. ref_walk_ses(rw) = ctx->dfs_root_ses;
  105. return 0;
  106. }
  107. static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
  108. struct dfs_ref_walk *rw)
  109. {
  110. struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
  111. struct dfs_info3_param tgt = {};
  112. int rc = -ENOENT;
  113. again:
  114. do {
  115. ctx->dfs_root_ses = ref_walk_ses(rw);
  116. if (ref_walk_empty(rw)) {
  117. rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1,
  118. NULL, ref_walk_tl(rw));
  119. if (rc) {
  120. rc = cifs_mount_get_tcon(mnt_ctx);
  121. if (!rc)
  122. rc = cifs_is_path_remote(mnt_ctx);
  123. continue;
  124. }
  125. if (!ref_walk_num_tgts(rw)) {
  126. rc = -ENOENT;
  127. continue;
  128. }
  129. }
  130. while (ref_walk_next_tgt(rw)) {
  131. rc = parse_dfs_target(ctx, rw, &tgt);
  132. if (rc)
  133. continue;
  134. cifs_mount_put_conns(mnt_ctx);
  135. rc = get_session(mnt_ctx, ref_walk_path(rw));
  136. if (rc)
  137. continue;
  138. ref_walk_set_tgt_hint(rw);
  139. if (tgt.flags & DFSREF_STORAGE_SERVER) {
  140. rc = cifs_mount_get_tcon(mnt_ctx);
  141. if (!rc)
  142. rc = cifs_is_path_remote(mnt_ctx);
  143. if (!rc)
  144. break;
  145. if (rc != -EREMOTE)
  146. continue;
  147. }
  148. set_root_smb_session(mnt_ctx);
  149. rc = ref_walk_advance(rw);
  150. if (!rc) {
  151. rc = setup_dfs_ref(mnt_ctx, &tgt, rw);
  152. if (!rc) {
  153. rc = -EREMOTE;
  154. goto again;
  155. }
  156. }
  157. if (rc != -ELOOP)
  158. goto out;
  159. }
  160. } while (rc && ref_walk_descend(rw));
  161. out:
  162. free_dfs_info_param(&tgt);
  163. return rc;
  164. }
  165. static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
  166. struct dfs_ref_walk **rw)
  167. {
  168. int rc;
  169. *rw = ref_walk_alloc();
  170. if (IS_ERR(*rw)) {
  171. rc = PTR_ERR(*rw);
  172. *rw = NULL;
  173. return rc;
  174. }
  175. ref_walk_init(*rw);
  176. rc = setup_dfs_ref(mnt_ctx, NULL, *rw);
  177. if (!rc)
  178. rc = __dfs_referral_walk(mnt_ctx, *rw);
  179. return rc;
  180. }
  181. static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
  182. {
  183. struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
  184. struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
  185. struct dfs_ref_walk *rw = NULL;
  186. struct cifs_tcon *tcon;
  187. char *origin_fullpath;
  188. int rc;
  189. origin_fullpath = dfs_get_path(cifs_sb, ctx->source);
  190. if (IS_ERR(origin_fullpath))
  191. return PTR_ERR(origin_fullpath);
  192. rc = dfs_referral_walk(mnt_ctx, &rw);
  193. if (!rc) {
  194. /*
  195. * Prevent superblock from being created with any missing
  196. * connections.
  197. */
  198. if (WARN_ON(!mnt_ctx->server))
  199. rc = -EHOSTDOWN;
  200. else if (WARN_ON(!mnt_ctx->ses))
  201. rc = -EACCES;
  202. else if (WARN_ON(!mnt_ctx->tcon))
  203. rc = -ENOENT;
  204. }
  205. if (rc)
  206. goto out;
  207. tcon = mnt_ctx->tcon;
  208. spin_lock(&tcon->tc_lock);
  209. tcon->origin_fullpath = origin_fullpath;
  210. origin_fullpath = NULL;
  211. ref_walk_set_tcon(rw, tcon);
  212. spin_unlock(&tcon->tc_lock);
  213. queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
  214. dfs_cache_get_ttl() * HZ);
  215. out:
  216. kfree(origin_fullpath);
  217. ref_walk_free(rw);
  218. return rc;
  219. }
  220. /*
  221. * If @ctx->dfs_automount, then update @ctx->dstaddr earlier with the DFS root
  222. * server from where we'll start following any referrals. Otherwise rely on the
  223. * value provided by mount(2) as the user might not have dns_resolver key set up
  224. * and therefore failing to upcall to resolve UNC hostname under @ctx->source.
  225. */
  226. static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
  227. {
  228. struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
  229. int rc = 0;
  230. if (!ctx->nodfs && ctx->dfs_automount) {
  231. rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
  232. if (!rc)
  233. cifs_set_port(addr, ctx->port);
  234. ctx->dfs_automount = false;
  235. }
  236. return rc;
  237. }
  238. int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
  239. {
  240. struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
  241. bool nodfs = ctx->nodfs;
  242. int rc;
  243. rc = update_fs_context_dstaddr(ctx);
  244. if (rc)
  245. return rc;
  246. rc = get_session(mnt_ctx, NULL);
  247. if (rc)
  248. return rc;
  249. /*
  250. * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally
  251. * try to get an DFS referral (even cached) to determine whether it is an DFS mount.
  252. *
  253. * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
  254. * to respond with PATH_NOT_COVERED to requests that include the prefix.
  255. */
  256. if (!nodfs) {
  257. rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL);
  258. if (rc) {
  259. cifs_dbg(FYI, "%s: no dfs referral for %s: %d\n",
  260. __func__, ctx->UNC + 1, rc);
  261. cifs_dbg(FYI, "%s: assuming non-dfs mount...\n", __func__);
  262. nodfs = true;
  263. }
  264. }
  265. if (nodfs) {
  266. rc = cifs_mount_get_tcon(mnt_ctx);
  267. if (!rc)
  268. rc = cifs_is_path_remote(mnt_ctx);
  269. return rc;
  270. }
  271. if (!ctx->dfs_conn) {
  272. ctx->dfs_conn = true;
  273. cifs_mount_put_conns(mnt_ctx);
  274. rc = get_session(mnt_ctx, NULL);
  275. }
  276. if (!rc) {
  277. set_root_smb_session(mnt_ctx);
  278. rc = __dfs_mount_share(mnt_ctx);
  279. }
  280. return rc;
  281. }
  282. static int target_share_matches_server(struct TCP_Server_Info *server, char *share,
  283. bool *target_match)
  284. {
  285. int rc = 0;
  286. const char *dfs_host;
  287. size_t dfs_host_len;
  288. *target_match = true;
  289. extract_unc_hostname(share, &dfs_host, &dfs_host_len);
  290. /* Check if hostnames or addresses match */
  291. cifs_server_lock(server);
  292. if (dfs_host_len != strlen(server->hostname) ||
  293. strncasecmp(dfs_host, server->hostname, dfs_host_len)) {
  294. cifs_dbg(FYI, "%s: %.*s doesn't match %s\n", __func__,
  295. (int)dfs_host_len, dfs_host, server->hostname);
  296. rc = match_target_ip(server, dfs_host, dfs_host_len, target_match);
  297. if (rc)
  298. cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
  299. }
  300. cifs_server_unlock(server);
  301. return rc;
  302. }
  303. static int tree_connect_dfs_target(const unsigned int xid,
  304. struct cifs_tcon *tcon,
  305. struct cifs_sb_info *cifs_sb,
  306. char *tree, bool islink,
  307. struct dfs_cache_tgt_list *tl)
  308. {
  309. const struct smb_version_operations *ops = tcon->ses->server->ops;
  310. struct TCP_Server_Info *server = tcon->ses->server;
  311. struct dfs_cache_tgt_iterator *tit;
  312. char *share = NULL, *prefix = NULL;
  313. bool target_match;
  314. int rc = -ENOENT;
  315. /* Try to tree connect to all dfs targets */
  316. for (tit = dfs_cache_get_tgt_iterator(tl);
  317. tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
  318. kfree(share);
  319. kfree(prefix);
  320. share = prefix = NULL;
  321. /* Check if share matches with tcp ses */
  322. rc = dfs_cache_get_tgt_share(server->leaf_fullpath + 1, tit, &share, &prefix);
  323. if (rc) {
  324. cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc);
  325. break;
  326. }
  327. rc = target_share_matches_server(server, share, &target_match);
  328. if (rc)
  329. break;
  330. if (!target_match) {
  331. rc = -EHOSTUNREACH;
  332. continue;
  333. }
  334. dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit);
  335. scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
  336. rc = ops->tree_connect(xid, tcon->ses, tree,
  337. tcon, tcon->ses->local_nls);
  338. if (islink && !rc && cifs_sb)
  339. rc = cifs_update_super_prepath(cifs_sb, prefix);
  340. break;
  341. }
  342. kfree(share);
  343. kfree(prefix);
  344. dfs_cache_free_tgts(tl);
  345. return rc;
  346. }
  347. int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
  348. {
  349. int rc;
  350. struct TCP_Server_Info *server = tcon->ses->server;
  351. const struct smb_version_operations *ops = server->ops;
  352. DFS_CACHE_TGT_LIST(tl);
  353. struct cifs_sb_info *cifs_sb = NULL;
  354. struct super_block *sb = NULL;
  355. struct dfs_info3_param ref = {0};
  356. char *tree;
  357. /* only send once per connect */
  358. spin_lock(&tcon->tc_lock);
  359. /* if tcon is marked for needing reconnect, update state */
  360. if (tcon->need_reconnect)
  361. tcon->status = TID_NEED_TCON;
  362. if (tcon->status == TID_GOOD) {
  363. spin_unlock(&tcon->tc_lock);
  364. return 0;
  365. }
  366. if (tcon->status != TID_NEW &&
  367. tcon->status != TID_NEED_TCON) {
  368. spin_unlock(&tcon->tc_lock);
  369. return -EHOSTDOWN;
  370. }
  371. tcon->status = TID_IN_TCON;
  372. spin_unlock(&tcon->tc_lock);
  373. tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
  374. if (!tree) {
  375. rc = -ENOMEM;
  376. goto out;
  377. }
  378. if (tcon->ipc) {
  379. cifs_server_lock(server);
  380. scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
  381. cifs_server_unlock(server);
  382. rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
  383. goto out;
  384. }
  385. sb = cifs_get_dfs_tcon_super(tcon);
  386. if (!IS_ERR(sb))
  387. cifs_sb = CIFS_SB(sb);
  388. /* Tree connect to last share in @tcon->tree_name if no DFS referral */
  389. if (!server->leaf_fullpath ||
  390. dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) {
  391. rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name,
  392. tcon, tcon->ses->local_nls);
  393. goto out;
  394. }
  395. rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK,
  396. &tl);
  397. free_dfs_info_param(&ref);
  398. out:
  399. kfree(tree);
  400. cifs_put_tcp_super(sb);
  401. if (rc) {
  402. spin_lock(&tcon->tc_lock);
  403. if (tcon->status == TID_IN_TCON)
  404. tcon->status = TID_NEED_TCON;
  405. spin_unlock(&tcon->tc_lock);
  406. } else {
  407. spin_lock(&tcon->tc_lock);
  408. if (tcon->status == TID_IN_TCON)
  409. tcon->status = TID_GOOD;
  410. tcon->need_reconnect = false;
  411. spin_unlock(&tcon->tc_lock);
  412. }
  413. return rc;
  414. }