1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de> 4 */ 5 6 #include <linux/namei.h> 7 #include "cifsproto.h" 8 #include "cifs_debug.h" 9 #include "dns_resolve.h" 10 #include "fs_context.h" 11 #include "dfs.h" 12 13 /** 14 * dfs_parse_target_referral - set fs context for dfs target referral 15 * 16 * @full_path: full path in UNC format. 17 * @ref: dfs referral pointer. 18 * @ctx: smb3 fs context pointer. 19 * 20 * Return zero if dfs referral was parsed correctly, otherwise non-zero. 21 */ 22 int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref, 23 struct smb3_fs_context *ctx) 24 { 25 int rc; 26 const char *prepath = NULL; 27 char *path; 28 29 if (!full_path || !*full_path || !ref || !ctx) 30 return -EINVAL; 31 32 if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0)) 33 return -EINVAL; 34 35 if (strlen(full_path) - ref->path_consumed) { 36 prepath = full_path + ref->path_consumed; 37 /* skip initial delimiter */ 38 if (*prepath == '/' || *prepath == '\\') 39 prepath++; 40 } 41 42 path = cifs_build_devname(ref->node_name, prepath); 43 if (IS_ERR(path)) 44 return PTR_ERR(path); 45 46 rc = smb3_parse_devname(path, ctx); 47 if (rc) 48 goto out; 49 50 rc = dns_resolve_server_name_to_ip(path, (struct sockaddr *)&ctx->dstaddr, NULL); 51 52 out: 53 kfree(path); 54 return rc; 55 } 56 57 static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) 58 { 59 struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 60 int rc; 61 62 ctx->leaf_fullpath = (char *)full_path; 63 rc = cifs_mount_get_session(mnt_ctx); 64 ctx->leaf_fullpath = NULL; 65 66 return rc; 67 } 68 69 static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx) 70 { 71 struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 72 struct dfs_root_ses *root_ses; 73 struct cifs_ses *ses = mnt_ctx->ses; 74 75 if (ses) { 76 root_ses = kmalloc(sizeof(*root_ses), GFP_KERNEL); 77 if (!root_ses) 78 return -ENOMEM; 79 80 INIT_LIST_HEAD(&root_ses->list); 81 82 spin_lock(&cifs_tcp_ses_lock); 83 ses->ses_count++; 84 spin_unlock(&cifs_tcp_ses_lock); 85 root_ses->ses = ses; 86 list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list); 87 } 88 ctx->dfs_root_ses = ses; 89 return 0; 90 } 91 92 static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, const char *full_path, 93 const struct dfs_cache_tgt_iterator *tit) 94 { 95 struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 96 struct dfs_info3_param ref = {}; 97 bool is_refsrv; 98 int rc, rc2; 99 100 rc = dfs_cache_get_tgt_referral(ref_path + 1, tit, &ref); 101 if (rc) 102 return rc; 103 104 rc = dfs_parse_target_referral(full_path + 1, &ref, ctx); 105 if (rc) 106 goto out; 107 108 cifs_mount_put_conns(mnt_ctx); 109 rc = get_session(mnt_ctx, ref_path); 110 if (rc) 111 goto out; 112 113 is_refsrv = !!(ref.flags & DFSREF_REFERRAL_SERVER); 114 115 rc = -EREMOTE; 116 if (ref.flags & DFSREF_STORAGE_SERVER) { 117 rc = cifs_mount_get_tcon(mnt_ctx); 118 if (rc) 119 goto out; 120 121 /* some servers may not advertise referral capability under ref.flags */ 122 is_refsrv |= is_tcon_dfs(mnt_ctx->tcon); 123 124 rc = cifs_is_path_remote(mnt_ctx); 125 } 126 127 dfs_cache_noreq_update_tgthint(ref_path + 1, tit); 128 129 if (rc == -EREMOTE && is_refsrv) { 130 rc2 = add_root_smb_session(mnt_ctx); 131 if (rc2) 132 rc = rc2; 133 } 134 135 out: 136 free_dfs_info_param(&ref); 137 return rc; 138 } 139 140 static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) 141 { 142 struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 143 struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 144 char *ref_path = NULL, *full_path = NULL; 145 struct dfs_cache_tgt_iterator *tit; 146 struct TCP_Server_Info *server; 147 struct cifs_tcon *tcon; 148 char *origin_fullpath = NULL; 149 char sep = CIFS_DIR_SEP(cifs_sb); 150 int num_links = 0; 151 int rc; 152 153 ref_path = dfs_get_path(cifs_sb, ctx->UNC); 154 if (IS_ERR(ref_path)) 155 return PTR_ERR(ref_path); 156 157 full_path = smb3_fs_context_fullpath(ctx, sep); 158 if (IS_ERR(full_path)) { 159 rc = PTR_ERR(full_path); 160 full_path = NULL; 161 goto out; 162 } 163 164 origin_fullpath = kstrdup(full_path, GFP_KERNEL); 165 if (!origin_fullpath) { 166 rc = -ENOMEM; 167 goto out; 168 } 169 170 do { 171 struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); 172 173 rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl); 174 if (rc) 175 break; 176 177 tit = dfs_cache_get_tgt_iterator(&tl); 178 if (!tit) { 179 cifs_dbg(VFS, "%s: dfs referral (%s) with no targets\n", __func__, 180 ref_path + 1); 181 rc = -ENOENT; 182 dfs_cache_free_tgts(&tl); 183 break; 184 } 185 186 do { 187 rc = get_dfs_conn(mnt_ctx, ref_path, full_path, tit); 188 if (!rc) 189 break; 190 if (rc == -EREMOTE) { 191 if (++num_links > MAX_NESTED_LINKS) { 192 rc = -ELOOP; 193 break; 194 } 195 kfree(ref_path); 196 kfree(full_path); 197 ref_path = full_path = NULL; 198 199 full_path = smb3_fs_context_fullpath(ctx, sep); 200 if (IS_ERR(full_path)) { 201 rc = PTR_ERR(full_path); 202 full_path = NULL; 203 } else { 204 ref_path = dfs_get_path(cifs_sb, full_path); 205 if (IS_ERR(ref_path)) { 206 rc = PTR_ERR(ref_path); 207 ref_path = NULL; 208 } 209 } 210 break; 211 } 212 } while ((tit = dfs_cache_get_next_tgt(&tl, tit))); 213 dfs_cache_free_tgts(&tl); 214 } while (rc == -EREMOTE); 215 216 if (!rc) { 217 server = mnt_ctx->server; 218 tcon = mnt_ctx->tcon; 219 220 spin_lock(&tcon->tc_lock); 221 if (!tcon->origin_fullpath) { 222 tcon->origin_fullpath = origin_fullpath; 223 origin_fullpath = NULL; 224 } 225 spin_unlock(&tcon->tc_lock); 226 227 if (list_empty(&tcon->dfs_ses_list)) { 228 list_replace_init(&mnt_ctx->dfs_ses_list, 229 &tcon->dfs_ses_list); 230 queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, 231 dfs_cache_get_ttl() * HZ); 232 } else { 233 dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list); 234 } 235 } 236 237 out: 238 kfree(origin_fullpath); 239 kfree(ref_path); 240 kfree(full_path); 241 return rc; 242 } 243 244 int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) 245 { 246 struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 247 struct cifs_ses *ses; 248 bool nodfs = ctx->nodfs; 249 int rc; 250 251 *isdfs = false; 252 rc = get_session(mnt_ctx, NULL); 253 if (rc) 254 return rc; 255 256 ctx->dfs_root_ses = mnt_ctx->ses; 257 /* 258 * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally 259 * try to get an DFS referral (even cached) to determine whether it is an DFS mount. 260 * 261 * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem 262 * to respond with PATH_NOT_COVERED to requests that include the prefix. 263 */ 264 if (!nodfs) { 265 rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL); 266 if (rc) { 267 cifs_dbg(FYI, "%s: no dfs referral for %s: %d\n", 268 __func__, ctx->UNC + 1, rc); 269 cifs_dbg(FYI, "%s: assuming non-dfs mount...\n", __func__); 270 nodfs = true; 271 } 272 } 273 if (nodfs) { 274 rc = cifs_mount_get_tcon(mnt_ctx); 275 if (!rc) 276 rc = cifs_is_path_remote(mnt_ctx); 277 return rc; 278 } 279 280 *isdfs = true; 281 /* 282 * Prevent DFS root session of being put in the first call to 283 * cifs_mount_put_conns(). If another DFS root server was not found 284 * while chasing the referrals (@ctx->dfs_root_ses == @ses), then we 285 * can safely put extra refcount of @ses. 286 */ 287 ses = mnt_ctx->ses; 288 mnt_ctx->ses = NULL; 289 mnt_ctx->server = NULL; 290 rc = __dfs_mount_share(mnt_ctx); 291 if (ses == ctx->dfs_root_ses) 292 cifs_put_smb_ses(ses); 293 294 return rc; 295 } 296 297 /* Update dfs referral path of superblock */ 298 static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, 299 const char *target) 300 { 301 int rc = 0; 302 size_t len = strlen(target); 303 char *refpath, *npath; 304 305 if (unlikely(len < 2 || *target != '\\')) 306 return -EINVAL; 307 308 if (target[1] == '\\') { 309 len += 1; 310 refpath = kmalloc(len, GFP_KERNEL); 311 if (!refpath) 312 return -ENOMEM; 313 314 scnprintf(refpath, len, "%s", target); 315 } else { 316 len += sizeof("\\"); 317 refpath = kmalloc(len, GFP_KERNEL); 318 if (!refpath) 319 return -ENOMEM; 320 321 scnprintf(refpath, len, "\\%s", target); 322 } 323 324 npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); 325 kfree(refpath); 326 327 if (IS_ERR(npath)) { 328 rc = PTR_ERR(npath); 329 } else { 330 mutex_lock(&server->refpath_lock); 331 spin_lock(&server->srv_lock); 332 kfree(server->leaf_fullpath); 333 server->leaf_fullpath = npath; 334 spin_unlock(&server->srv_lock); 335 mutex_unlock(&server->refpath_lock); 336 } 337 return rc; 338 } 339 340 static int target_share_matches_server(struct TCP_Server_Info *server, char *share, 341 bool *target_match) 342 { 343 int rc = 0; 344 const char *dfs_host; 345 size_t dfs_host_len; 346 347 *target_match = true; 348 extract_unc_hostname(share, &dfs_host, &dfs_host_len); 349 350 /* Check if hostnames or addresses match */ 351 cifs_server_lock(server); 352 if (dfs_host_len != strlen(server->hostname) || 353 strncasecmp(dfs_host, server->hostname, dfs_host_len)) { 354 cifs_dbg(FYI, "%s: %.*s doesn't match %s\n", __func__, 355 (int)dfs_host_len, dfs_host, server->hostname); 356 rc = match_target_ip(server, dfs_host, dfs_host_len, target_match); 357 if (rc) 358 cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); 359 } 360 cifs_server_unlock(server); 361 return rc; 362 } 363 364 static void __tree_connect_ipc(const unsigned int xid, char *tree, 365 struct cifs_sb_info *cifs_sb, 366 struct cifs_ses *ses) 367 { 368 struct TCP_Server_Info *server = ses->server; 369 struct cifs_tcon *tcon = ses->tcon_ipc; 370 int rc; 371 372 spin_lock(&ses->ses_lock); 373 spin_lock(&ses->chan_lock); 374 if (cifs_chan_needs_reconnect(ses, server) || 375 ses->ses_status != SES_GOOD) { 376 spin_unlock(&ses->chan_lock); 377 spin_unlock(&ses->ses_lock); 378 cifs_server_dbg(FYI, "%s: skipping ipc reconnect due to disconnected ses\n", 379 __func__); 380 return; 381 } 382 spin_unlock(&ses->chan_lock); 383 spin_unlock(&ses->ses_lock); 384 385 cifs_server_lock(server); 386 scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); 387 cifs_server_unlock(server); 388 389 rc = server->ops->tree_connect(xid, ses, tree, tcon, 390 cifs_sb->local_nls); 391 cifs_server_dbg(FYI, "%s: tree_reconnect %s: %d\n", __func__, tree, rc); 392 spin_lock(&tcon->tc_lock); 393 if (rc) { 394 tcon->status = TID_NEED_TCON; 395 } else { 396 tcon->status = TID_GOOD; 397 tcon->need_reconnect = false; 398 } 399 spin_unlock(&tcon->tc_lock); 400 } 401 402 static void tree_connect_ipc(const unsigned int xid, char *tree, 403 struct cifs_sb_info *cifs_sb, 404 struct cifs_tcon *tcon) 405 { 406 struct cifs_ses *ses = tcon->ses; 407 408 __tree_connect_ipc(xid, tree, cifs_sb, ses); 409 __tree_connect_ipc(xid, tree, cifs_sb, CIFS_DFS_ROOT_SES(ses)); 410 } 411 412 static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, 413 struct cifs_sb_info *cifs_sb, char *tree, bool islink, 414 struct dfs_cache_tgt_list *tl) 415 { 416 int rc; 417 struct TCP_Server_Info *server = tcon->ses->server; 418 const struct smb_version_operations *ops = server->ops; 419 struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses); 420 char *share = NULL, *prefix = NULL; 421 struct dfs_cache_tgt_iterator *tit; 422 bool target_match; 423 424 tit = dfs_cache_get_tgt_iterator(tl); 425 if (!tit) { 426 rc = -ENOENT; 427 goto out; 428 } 429 430 /* Try to tree connect to all dfs targets */ 431 for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { 432 const char *target = dfs_cache_get_tgt_name(tit); 433 struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); 434 435 kfree(share); 436 kfree(prefix); 437 share = prefix = NULL; 438 439 /* Check if share matches with tcp ses */ 440 rc = dfs_cache_get_tgt_share(server->leaf_fullpath + 1, tit, &share, &prefix); 441 if (rc) { 442 cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc); 443 break; 444 } 445 446 rc = target_share_matches_server(server, share, &target_match); 447 if (rc) 448 break; 449 if (!target_match) { 450 rc = -EHOSTUNREACH; 451 continue; 452 } 453 454 dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit); 455 tree_connect_ipc(xid, tree, cifs_sb, tcon); 456 457 scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); 458 if (!islink) { 459 rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); 460 break; 461 } 462 463 /* 464 * If no dfs referrals were returned from link target, then just do a TREE_CONNECT 465 * to it. Otherwise, cache the dfs referral and then mark current tcp ses for 466 * reconnect so either the demultiplex thread or the echo worker will reconnect to 467 * newly resolved target. 468 */ 469 if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, 470 NULL, &ntl)) { 471 rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); 472 if (rc) 473 continue; 474 475 rc = cifs_update_super_prepath(cifs_sb, prefix); 476 } else { 477 /* Target is another dfs share */ 478 rc = update_server_fullpath(server, cifs_sb, target); 479 dfs_cache_free_tgts(tl); 480 481 if (!rc) { 482 rc = -EREMOTE; 483 list_replace_init(&ntl.tl_list, &tl->tl_list); 484 } else 485 dfs_cache_free_tgts(&ntl); 486 } 487 break; 488 } 489 490 out: 491 kfree(share); 492 kfree(prefix); 493 494 return rc; 495 } 496 497 static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, 498 struct cifs_sb_info *cifs_sb, char *tree, bool islink, 499 struct dfs_cache_tgt_list *tl) 500 { 501 int rc; 502 int num_links = 0; 503 struct TCP_Server_Info *server = tcon->ses->server; 504 char *old_fullpath = server->leaf_fullpath; 505 506 do { 507 rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl); 508 if (!rc || rc != -EREMOTE) 509 break; 510 } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); 511 /* 512 * If we couldn't tree connect to any targets from last referral path, then 513 * retry it from newly resolved dfs referral. 514 */ 515 if (rc && server->leaf_fullpath != old_fullpath) 516 cifs_signal_cifsd_for_reconnect(server, true); 517 518 dfs_cache_free_tgts(tl); 519 return rc; 520 } 521 522 int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) 523 { 524 int rc; 525 struct TCP_Server_Info *server = tcon->ses->server; 526 const struct smb_version_operations *ops = server->ops; 527 struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); 528 struct cifs_sb_info *cifs_sb = NULL; 529 struct super_block *sb = NULL; 530 struct dfs_info3_param ref = {0}; 531 char *tree; 532 533 /* only send once per connect */ 534 spin_lock(&tcon->tc_lock); 535 if (tcon->status == TID_GOOD) { 536 spin_unlock(&tcon->tc_lock); 537 return 0; 538 } 539 540 if (tcon->status != TID_NEW && 541 tcon->status != TID_NEED_TCON) { 542 spin_unlock(&tcon->tc_lock); 543 return -EHOSTDOWN; 544 } 545 546 tcon->status = TID_IN_TCON; 547 spin_unlock(&tcon->tc_lock); 548 549 tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); 550 if (!tree) { 551 rc = -ENOMEM; 552 goto out; 553 } 554 555 if (tcon->ipc) { 556 cifs_server_lock(server); 557 scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); 558 cifs_server_unlock(server); 559 rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); 560 goto out; 561 } 562 563 sb = cifs_get_dfs_tcon_super(tcon); 564 if (!IS_ERR(sb)) 565 cifs_sb = CIFS_SB(sb); 566 567 /* 568 * Tree connect to last share in @tcon->tree_name whether dfs super or 569 * cached dfs referral was not found. 570 */ 571 if (!cifs_sb || !server->leaf_fullpath || 572 dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) { 573 rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, 574 cifs_sb ? cifs_sb->local_nls : nlsc); 575 goto out; 576 } 577 578 rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK, 579 &tl); 580 free_dfs_info_param(&ref); 581 582 out: 583 kfree(tree); 584 cifs_put_tcp_super(sb); 585 586 if (rc) { 587 spin_lock(&tcon->tc_lock); 588 if (tcon->status == TID_IN_TCON) 589 tcon->status = TID_NEED_TCON; 590 spin_unlock(&tcon->tc_lock); 591 } else { 592 spin_lock(&tcon->tc_lock); 593 if (tcon->status == TID_IN_TCON) 594 tcon->status = TID_GOOD; 595 spin_unlock(&tcon->tc_lock); 596 tcon->need_reconnect = false; 597 } 598 599 return rc; 600 } 601