1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 29 #include <sys/mdb_modapi.h> 30 #include <mdb/mdb_ctf.h> 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 34 #include <netsmb/smb_conn.h> 35 #include <netsmb/smb_rq.h> 36 #include <netsmb/smb_pass.h> 37 38 #ifdef _KERNEL 39 #define NSMB_OBJNAME "nsmb" 40 #else 41 #define NSMB_OBJNAME "libfknsmb.so.1" 42 #endif 43 44 #define OPT_VERBOSE 0x0001 /* Be [-v]erbose in dcmd's */ 45 #define OPT_RECURSE 0x0002 /* recursive display */ 46 47 /* 48 * We need to read in a private copy 49 * of every string we want to print out. 50 */ 51 void 52 print_str(uintptr_t addr) 53 { 54 char buf[32]; 55 int len, mx = sizeof (buf) - 4; 56 57 if ((len = mdb_readstr(buf, sizeof (buf), addr)) <= 0) { 58 mdb_printf(" (%p)", addr); 59 } else { 60 if (len > mx) 61 strcpy(&buf[mx], "..."); 62 mdb_printf(" %s", buf); 63 } 64 } 65 66 67 /* 68 * Walker for smb_connobj_t structures, including 69 * smb_vc_t and smb_share_t which "inherit" from it. 70 * Tricky: Exploit the "inheritance" of smb_connobj_t 71 * with common functions for walk_init, walk_next. 72 */ 73 typedef struct smb_co_walk_data { 74 uintptr_t pp; 75 int level; /* SMBL_SM, SMBL_VC, SMBL_SHARE */ 76 int size; /* sizeof (union member) */ 77 union co_u { 78 smb_connobj_t co; /* copy of the list element */ 79 smb_vc_t vc; 80 smb_share_t ss; 81 } u; 82 } smb_co_walk_data_t; 83 84 /* 85 * Common walk_init for walking structs inherited 86 * from smb_connobj_t (smb_vc_t, smb_share_t) 87 */ 88 int 89 smb_co_walk_init(mdb_walk_state_t *wsp, int level) 90 { 91 smb_co_walk_data_t *smbw; 92 size_t psz; 93 94 if (wsp->walk_addr == 0) 95 return (WALK_ERR); 96 97 smbw = mdb_alloc(sizeof (*smbw), UM_SLEEP | UM_GC); 98 wsp->walk_data = smbw; 99 100 /* 101 * Save the parent pointer for later checks, and 102 * the level so we know which union member it is. 103 * Also the size of this union member. 104 */ 105 smbw->pp = wsp->walk_addr; 106 smbw->level = level; 107 switch (level) { 108 case SMBL_SM: 109 smbw->size = sizeof (smbw->u.co); 110 break; 111 case SMBL_VC: 112 smbw->size = sizeof (smbw->u.vc); 113 break; 114 case SMBL_SHARE: 115 smbw->size = sizeof (smbw->u.ss); 116 break; 117 default: 118 smbw->size = sizeof (smbw->u); 119 break; 120 } 121 122 /* 123 * Read in the parent object. Just need the 124 * invariant part (smb_connobj_t) so we can 125 * get the list of children below it. 126 */ 127 psz = sizeof (smbw->u.co); 128 if (mdb_vread(&smbw->u.co, psz, smbw->pp) != psz) { 129 mdb_warn("cannot read connobj from %p", smbw->pp); 130 return (WALK_ERR); 131 } 132 133 /* 134 * Finally, setup to walk the list of children. 135 */ 136 wsp->walk_addr = (uintptr_t)smbw->u.co.co_children.slh_first; 137 138 return (WALK_NEXT); 139 } 140 141 /* 142 * Walk the (global) VC list. 143 */ 144 int 145 smb_vc_walk_init(mdb_walk_state_t *wsp) 146 { 147 GElf_Sym sym; 148 149 if (wsp->walk_addr != 0) { 150 mdb_warn("::walk smb_vc only supports global walks\n"); 151 return (WALK_ERR); 152 } 153 154 /* Locate the VC list head. */ 155 if (mdb_lookup_by_obj(NSMB_OBJNAME, "smb_vclist", &sym)) { 156 mdb_warn("failed to lookup `smb_vclist'\n"); 157 return (WALK_ERR); 158 } 159 wsp->walk_addr = sym.st_value; 160 161 return (smb_co_walk_init(wsp, SMBL_VC)); 162 } 163 164 /* 165 * Walk the share list below some VC. 166 */ 167 int 168 smb_ss_walk_init(mdb_walk_state_t *wsp) 169 { 170 171 /* 172 * Initial walk_addr is address of parent (VC) 173 */ 174 if (wsp->walk_addr == 0) { 175 mdb_warn("::walk smb_ss does not support global walks\n"); 176 return (WALK_ERR); 177 } 178 179 return (smb_co_walk_init(wsp, SMBL_SHARE)); 180 } 181 182 /* 183 * Common walk_step for walking structs inherited 184 * from smb_connobj_t (smb_vc_t, smb_share_t) 185 */ 186 int 187 smb_co_walk_step(mdb_walk_state_t *wsp) 188 { 189 smb_co_walk_data_t *smbw = wsp->walk_data; 190 int status; 191 192 if (wsp->walk_addr == 0) 193 return (WALK_DONE); 194 195 if (mdb_vread(&smbw->u, smbw->size, wsp->walk_addr) 196 != smbw->size) { 197 mdb_warn("cannot read connobj from %p", wsp->walk_addr); 198 return (WALK_ERR); 199 } 200 201 /* XXX: Sanity check level? parent pointer? */ 202 203 status = wsp->walk_callback(wsp->walk_addr, &smbw->u, 204 wsp->walk_cbdata); 205 206 wsp->walk_addr = (uintptr_t)smbw->u.co.co_next.sle_next; 207 208 return (status); 209 } 210 211 212 /* 213 * Dcmd (and callback function) to print a summary of 214 * all VCs, and optionally all shares under each VC. 215 */ 216 217 typedef struct smb_co_cbdata { 218 int flags; /* OPT_... */ 219 int printed_header; 220 mdb_ctf_id_t ctf_id; 221 } smb_co_cbdata_t; 222 223 /* 224 * Call-back function for walking a share list. 225 */ 226 int 227 smb_ss_cb(uintptr_t addr, const void *data, void *arg) 228 { 229 const smb_share_t *ssp = data; 230 smb_co_cbdata_t *cbd = arg; 231 232 mdb_printf(" %-p\t%s\n", addr, ssp->ss_name); 233 234 if (cbd->flags & OPT_VERBOSE) { 235 mdb_inc_indent(2); 236 /* Anything wanted here? */ 237 mdb_dec_indent(2); 238 } 239 240 return (WALK_NEXT); 241 } 242 243 static const char * 244 vcstate_str(smb_co_cbdata_t *cbd, int stval) 245 { 246 static const char prefix[] = "SMBIOD_ST_"; 247 int prefix_len = sizeof (prefix) - 1; 248 mdb_ctf_id_t vcst_enum; 249 const char *cp; 250 251 /* Got this in smb_vc_dcmd. */ 252 vcst_enum = cbd->ctf_id; 253 254 /* Get the name for the enum value. */ 255 if ((cp = mdb_ctf_enum_name(vcst_enum, stval)) == NULL) 256 return ("?"); 257 258 /* Skip the prefix part. */ 259 if (strncmp(cp, prefix, prefix_len) == 0) 260 cp += prefix_len; 261 262 return (cp); 263 } 264 265 /* 266 * Call-back function for walking the VC list. 267 */ 268 int 269 smb_vc_cb(uintptr_t addr, const void *data, void *arg) 270 { 271 const smb_vc_t *vcp = data; 272 smb_co_cbdata_t *cbd = arg; 273 274 if (cbd->printed_header == 0) { 275 cbd->printed_header = 1; 276 mdb_printf("// smb_vc_t uid server \tuser\t\tstate\n"); 277 } 278 279 mdb_printf("%-p", addr); 280 mdb_printf(" %7d", vcp->vc_owner); 281 282 switch (vcp->vc_srvaddr.sa.sa_family) { 283 case AF_INET: 284 mdb_printf(" %I", vcp->vc_srvaddr.sin.sin_addr); 285 break; 286 case AF_INET6: 287 mdb_printf(" %N", &vcp->vc_srvaddr.sin6.sin6_addr); 288 break; 289 default: 290 mdb_printf(" %15s", "(bad af)"); 291 break; 292 } 293 294 if (vcp->vc_username[0] != '\0') 295 mdb_printf("\t%s", vcp->vc_username); 296 else 297 mdb_printf("\t%s", "(?)"); 298 299 if (vcp->vc_domain[0] != '\0') 300 mdb_printf("@%s", vcp->vc_domain); 301 302 mdb_printf("\t%s\n", vcstate_str(cbd, vcp->vc_state)); 303 304 if (cbd->flags & OPT_RECURSE) { 305 mdb_inc_indent(2); 306 if (mdb_pwalk("nsmb_ss", smb_ss_cb, cbd, addr) < 0) { 307 mdb_warn("failed to walk 'nsmb_ss'"); 308 /* Don't: return (WALK_ERR); */ 309 } 310 mdb_dec_indent(2); 311 } 312 313 return (WALK_NEXT); 314 } 315 316 int 317 smb_vc_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 318 { 319 smb_co_cbdata_t cbd; 320 smb_vc_t *vcp; 321 size_t vcsz; 322 323 memset(&cbd, 0, sizeof (cbd)); 324 325 if (mdb_getopts(argc, argv, 326 'r', MDB_OPT_SETBITS, OPT_RECURSE, &cbd.flags, 327 'v', MDB_OPT_SETBITS, OPT_VERBOSE, &cbd.flags, 328 NULL) != argc) { 329 return (DCMD_USAGE); 330 } 331 332 if (mdb_ctf_lookup_by_name("enum smbiod_state", &cbd.ctf_id) == -1) { 333 mdb_warn("Could not find enum smbiod_state"); 334 } 335 336 if (!(flags & DCMD_ADDRSPEC)) { 337 if (mdb_walk("nsmb_vc", smb_vc_cb, &cbd) == -1) { 338 mdb_warn("failed to walk 'nsmb_vc'"); 339 return (DCMD_ERR); 340 } 341 return (DCMD_OK); 342 } 343 344 vcsz = sizeof (*vcp); 345 vcp = mdb_alloc(vcsz, UM_SLEEP | UM_GC); 346 if (mdb_vread(vcp, vcsz, addr) != vcsz) { 347 mdb_warn("cannot read VC from %p", addr); 348 return (DCMD_ERR); 349 } 350 smb_vc_cb(addr, vcp, &cbd); 351 352 return (DCMD_OK); 353 } 354 355 void 356 smb_vc_help(void) 357 { 358 mdb_printf("Options:\n" 359 " -r recursive display of share lists\n" 360 " -v be verbose when displaying smb_vc\n"); 361 } 362 363 /* 364 * Walker for the request list on a VC, 365 * and dcmd to show a summary. 366 */ 367 int 368 rqlist_walk_init(mdb_walk_state_t *wsp) 369 { 370 struct smb_rqhead rqh; 371 uintptr_t addr; 372 373 /* 374 * Initial walk_addr is the address of the VC. 375 * Add offsetof(iod_rqlist) to get the rqhead. 376 */ 377 if (wsp->walk_addr == 0) { 378 mdb_warn("::walk smb_ss does not support global walks\n"); 379 return (WALK_ERR); 380 } 381 addr = wsp->walk_addr; 382 addr += OFFSETOF(smb_vc_t, iod_rqlist); 383 384 if (mdb_vread(&rqh, sizeof (rqh), addr) == -1) { 385 mdb_warn("failed to read smb_rqhead at %p", addr); 386 return (WALK_ERR); 387 } 388 wsp->walk_addr = (uintptr_t)rqh.tqh_first; 389 390 return (WALK_NEXT); 391 } 392 393 int 394 rqlist_walk_step(mdb_walk_state_t *wsp) 395 { 396 smb_rq_t rq; 397 int status; 398 399 if (wsp->walk_addr == 0) 400 return (WALK_DONE); 401 402 if (mdb_vread(&rq, sizeof (rq), wsp->walk_addr) == -1) { 403 mdb_warn("cannot read smb_rq from %p", wsp->walk_addr); 404 return (WALK_ERR); 405 } 406 407 status = wsp->walk_callback(wsp->walk_addr, &rq, 408 wsp->walk_cbdata); 409 410 wsp->walk_addr = (uintptr_t)rq.sr_link.tqe_next; 411 412 return (status); 413 } 414 415 typedef struct rqlist_cbdata { 416 int printed_header; 417 uintptr_t uid; /* optional filtering by UID */ 418 } rqlist_cbdata_t; 419 420 int 421 rqlist_cb(uintptr_t addr, const void *data, void *arg) 422 { 423 const smb_rq_t *rq = data; 424 rqlist_cbdata_t *cbd = arg; 425 426 if (cbd->printed_header == 0) { 427 cbd->printed_header = 1; 428 mdb_printf("// smb_rq_t MID cmd sr_state sr_flags\n"); 429 } 430 431 mdb_printf(" %-p", addr); /* smb_rq_t */ 432 mdb_printf(" x%04x", rq->sr_mid); 433 mdb_printf(" x%02x", rq->sr_cmd); 434 mdb_printf(" %d", rq->sr_state); 435 mdb_printf(" x%x", rq->sr_flags); 436 mdb_printf("\n"); 437 438 return (WALK_NEXT); 439 } 440 441 /*ARGSUSED*/ 442 int 443 rqlist_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 444 { 445 rqlist_cbdata_t cbd; 446 447 memset(&cbd, 0, sizeof (cbd)); 448 449 /* 450 * Initial walk_addr is address of parent (VC) 451 */ 452 if (!(flags & DCMD_ADDRSPEC)) { 453 mdb_warn("address required\n"); 454 return (DCMD_ERR); 455 } 456 457 if (mdb_pwalk("nsmb_rqlist", rqlist_cb, &cbd, addr) == -1) { 458 mdb_warn("failed to walk 'nsmb_rqlist'"); 459 return (DCMD_ERR); 460 } 461 462 return (DCMD_OK); 463 } 464 465 466 /* 467 * AVL walker for the passwords AVL tree, 468 * and dcmd to show a summary. 469 */ 470 static int 471 pwtree_walk_init(mdb_walk_state_t *wsp) 472 { 473 GElf_Sym sym; 474 475 if (wsp->walk_addr != 0) { 476 mdb_warn("pwtree walk only supports global walks\n"); 477 return (WALK_ERR); 478 } 479 480 if (mdb_lookup_by_obj(NSMB_OBJNAME, "smb_ptd", &sym) == -1) { 481 mdb_warn("failed to find symbol 'smb_ptd'"); 482 return (WALK_ERR); 483 } 484 485 wsp->walk_addr = (uintptr_t)sym.st_value; 486 487 if (mdb_layered_walk("avl", wsp) == -1) { 488 mdb_warn("failed to walk 'avl'\n"); 489 return (WALK_ERR); 490 } 491 492 return (WALK_NEXT); 493 } 494 495 static int 496 pwtree_walk_step(mdb_walk_state_t *wsp) 497 { 498 smb_passid_t ptnode; 499 500 if (mdb_vread(&ptnode, sizeof (ptnode), wsp->walk_addr) == -1) { 501 mdb_warn("failed to read smb_passid_t at %p", wsp->walk_addr); 502 return (WALK_ERR); 503 } 504 505 return (wsp->walk_callback(wsp->walk_addr, &ptnode, wsp->walk_cbdata)); 506 } 507 508 typedef struct pwtree_cbdata { 509 int printed_header; 510 uid_t uid; /* optional filtering by UID */ 511 } pwtree_cbdata_t; 512 513 int 514 pwtree_cb(uintptr_t addr, const void *data, void *arg) 515 { 516 const smb_passid_t *ptn = data; 517 pwtree_cbdata_t *cbd = arg; 518 519 /* Optional filtering by UID. */ 520 if (cbd->uid != (uid_t)-1 && cbd->uid != ptn->uid) { 521 return (WALK_NEXT); 522 } 523 524 if (cbd->printed_header == 0) { 525 cbd->printed_header = 1; 526 mdb_printf("// smb_passid_t UID domain user\n"); 527 } 528 529 mdb_printf(" %-p", addr); /* smb_passid_t */ 530 mdb_printf(" %d", (uintptr_t)ptn->uid); 531 print_str((uintptr_t)ptn->srvdom); 532 print_str((uintptr_t)ptn->username); 533 mdb_printf("\n"); 534 535 return (WALK_NEXT); 536 } 537 538 /*ARGSUSED*/ 539 int 540 pwtree_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 541 { 542 pwtree_cbdata_t cbd; 543 char *uid_str = NULL; 544 char buf[32]; 545 546 memset(&cbd, 0, sizeof (cbd)); 547 548 if (mdb_getopts(argc, argv, 549 'u', MDB_OPT_STR, &uid_str, NULL) != argc) { 550 return (DCMD_USAGE); 551 } 552 if (uid_str) { 553 /* 554 * Want the the default radix to be 10 here. 555 * If the string has some kind of radix prefix, 556 * just use that as-is, otherwise prepend "0t". 557 * Cheating on the "not a digit" test, but 558 * mdb_strtoull will do a real syntax check. 559 */ 560 if (uid_str[0] == '0' && uid_str[1] > '9') { 561 cbd.uid = (uid_t)mdb_strtoull(uid_str); 562 } else { 563 strcpy(buf, "0t"); 564 strlcat(buf, uid_str, sizeof (buf)); 565 cbd.uid = (uid_t)mdb_strtoull(buf); 566 } 567 } else 568 cbd.uid = (uid_t)-1; 569 570 if (flags & DCMD_ADDRSPEC) { 571 mdb_warn("address not allowed\n"); 572 return (DCMD_ERR); 573 } 574 575 if (mdb_pwalk("nsmb_pwtree", pwtree_cb, &cbd, 0) == -1) { 576 mdb_warn("failed to walk 'nsmb_pwtree'"); 577 return (DCMD_ERR); 578 } 579 580 return (DCMD_OK); 581 } 582 583 void 584 pwtree_help(void) 585 { 586 mdb_printf("Options:\n" 587 " -u uid show only entries belonging to uid (decimal)\n"); 588 } 589 590 591 static const mdb_dcmd_t dcmds[] = { 592 { "nsmb_vc", "?[-rv]", 593 "show smb_vc (or list)", 594 smb_vc_dcmd, smb_vc_help }, 595 { "nsmb_rqlist", ":", 596 "show smb_rq list on a VC", 597 rqlist_dcmd, NULL }, 598 { "nsmb_pwtree", "?[-u uid]", 599 "list smb_passid_t (password tree)", 600 pwtree_dcmd, pwtree_help }, 601 {NULL} 602 }; 603 604 static const mdb_walker_t walkers[] = { 605 { "nsmb_vc", "walk nsmb VC list", 606 smb_vc_walk_init, smb_co_walk_step, NULL }, 607 { "nsmb_ss", "walk nsmb share list for some VC", 608 smb_ss_walk_init, smb_co_walk_step, NULL }, 609 { "nsmb_rqlist", "walk request list for some VC", 610 rqlist_walk_init, rqlist_walk_step, NULL }, 611 { "nsmb_pwtree", "walk passord AVL tree", 612 pwtree_walk_init, pwtree_walk_step, NULL }, 613 {NULL} 614 }; 615 616 static const mdb_modinfo_t modinfo = { 617 MDB_API_VERSION, 618 dcmds, 619 walkers 620 }; 621 622 const mdb_modinfo_t * 623 _mdb_init(void) 624 { 625 return (&modinfo); 626 } 627