# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2013-2021 Tintri by DDN, Inc. All rights reserved. # SUBDIRS = smbadm smbd smbstat dtrace fksmbd bind-helper \ nvlprint testoplock test-crypt test-msgbuf MSGSUBDIRS = smbadm smbstat include ../Makefile.cmd include ./Makefile.subdirs # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Common definitions for all smbsrv commands # OBJS= $(SRCS:%.c=%.o) CFLAGS += $(CCVERBOSE) $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CSTD = $(CSTD_GNU99) # Hammerhead: rpath-link for GNU ld to resolve mech_krb5.so.1 (transitive dep # of libsmbns) and libscsi/libses in non-standard paths LDFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/gss -Wl,-rpath-link,$(ROOT)/usr/lib/smbsrv # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" # # Common targets for all smbsrv commands # .KEEP_STATE: all: $(PROG) install: all .WAIT $(ROOTUSRSBINPROG) clean: $(RM) $(OBJS) lint: lint_SRCS $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" .KEEP_STATE: all : TARGET = all check : TARGET = check clean : TARGET = clean clobber : TARGET = clobber install : TARGET = install install_h : TARGET = install_h lint : TARGET = lint _msg : TARGET = _msg all check clean clobber install install_h lint: $(SUBDIRS) $(filter-out .WAIT,$(SUBDIRS)): FRC @cd $@; pwd; VERSION='$(VERSION)' $(MAKE) $(TARGET) _msg: $(MSGSUBDIRS) FRC: # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2013 Nexenta Systems, Inc. All rights reserved. # PROG= bind-helper SRCS= bind-helper.c include ../../Makefile.cmd include ../Makefile.smbsrv.defs LDLIBS += -lsocket -lnsl ROOTLIBSMBSRVDIR = $(ROOTLIB)/smbsrv ROOTLIBSMBSRVPROG = $(PROG:%=$(ROOTLIBSMBSRVDIR)/%) .KEEP_STATE: all: $(PROG) clean: $(RM) $(OBJS) lint: lint_SRCS $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) include ../../Makefile.targ $(ROOTLIBSMBSRVDIR)/%: % $(INS.file) install: all .WAIT $(ROOTLIBSMBSRVPROG) /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * This program is installed with an RBAC exec_attr * that allows it to bind a reserved address. * (Or just make it setuid root.) * * To grant privileges to the program using RBAC, * add the following line to /etc/security/exec_attr * Forced Privilege:solaris:cmd:::\ * /usr/lib/smbsrv/bind-helper:\ * privs=net_privaddr,sys_smb\ * * Args: family address port * Does a bind on fileno(stdin) */ #include #include #include #include #include #include #include #include #include int main(int argc, char **argv) { struct sockaddr sa; /* LINTED E_BAD_PTR_CAST_ALIGN */ struct sockaddr_in *sin = (struct sockaddr_in *)&sa; /* LINTED E_BAD_PTR_CAST_ALIGN */ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa; int rc, err = 0; if (argc < 4) { (void) fprintf(stderr, "usage: %s family address port\n", argv[0]); exit(1); } (void) memset(&sa, 0, sizeof (sa)); sa.sa_family = atoi(argv[1]); switch (sa.sa_family) { case AF_INET: rc = inet_pton(AF_INET, argv[2], &sin->sin_addr); sin->sin_port = htons(atoi(argv[3])); break; case AF_INET6: rc = inet_pton(AF_INET6, argv[2], &sin6->sin6_addr); sin6->sin6_port = htons(atoi(argv[3])); break; default: rc = 0; break; } if (rc > 0) err = 0; else if (rc == 0) err = EINVAL; else if (rc < 0) err = errno; if (err != 0) { (void) fprintf(stderr, "%s: bad proto addr %s %s %s\n", argv[0], argv[1], argv[2], argv[3]); exit(1); } if (bind(0, &sa, sizeof (sa)) < 0) { err = errno; (void) fprintf(stderr, "%s: bind: %s\n", argv[0], strerror(err)); exit(2); } exit(0); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # SRCS= smbd-all.d smbd-authsvc.d smbd-doorsvc.d smbd-pipesvc.d \ smbnode.d smbsrv.d smbvfs.d smb-trace.d smb2-trace.d \ nbl-conflict.d include ../../Makefile.cmd ROOTSMBDTRACEDIR = $(ROOTLIB)/smbsrv/dtrace ROOTSMBDTRACEFILE = $(SRCS:%=$(ROOTSMBDTRACEDIR)/%) $(ROOTSMBDTRACEFILE): FILEMODE = 0555 $(ROOTSMBDTRACEDIR): $(INS.dir) $(ROOTSMBDTRACEDIR)/%: % $(INS.file) all: clean: lint: include ../../Makefile.targ install: all $(ROOTSMBDTRACEDIR) .WAIT $(ROOTSMBDTRACEFILE) #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2019 Nexenta Systems, Inc. All rights reserved. */ /* * This dtrace script shows how to track down the owners of locks * that prevent I/O in filesystems with mandatory locking enabled * (eg. ZFS with nbmand=on). This script is not in any way specific * to SMB, but this problem is most often seen when SMB is in use * because SMB requires mandatory locking semantics. * * Run this script, eg. dtrace -s nbl-conflict.d * taking note of these fields in the dtrace output: * conflict_lock: .l_sysid .l_pid * conflict_shrlock: .s_sysid .s_pid * * The sysid values tell you if a local or remote owner has the * lock or share preventing I/O, and the pid tells which process. */ sdt::nbl_lock_conflict:conflict_lock { this->lock = (lock_descriptor_t *)arg0; print(this->lock->l_flock); } sdt::nbl_share_conflict:conflict_shrlock { this->shrl = (struct shrlock *)arg0; print(*(this->shrl)); } /* * The above probe action in nbl_share_conflict shows conflicts * with read/write operations (eg. from the NFS server). * This probe action shows share reservation conflicts at open. * (Remove this if you're only interested in I/O conflicts.) */ sdt::add_share:conflict_shrlock { this->shrl = (struct shrlock *)arg0; print(*(this->shrl)); } #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * Example using the "smb" dtrace provider. * Traces all SMB commands. * * All these probes provide: * args[0] conninfo_t * args[1] smbopinfo_t * Some also provide one of: (not used here) * args[2] smb_name_args_t * args[2] smb_open_args_t * args[2] smb_rw_args_t * * Usage: smb-trace.d [|all [|all] []]] * * example: smb_trace.d 192.168.012.001 mypool_fs1 0 * * It is valid to specify or as "all" to * print data for all clients and/or all shares. * Omitting will print data for all zones. */ #pragma D option defaultargs dtrace:::BEGIN { all_clients = (($$1 == NULL) || ($$1 == "all")) ? 1 : 0; all_shares = (($$2 == NULL) || ($$2 == "all")) ? 1 : 0; all_zones = ($$3 == NULL) ? 1 : 0; client = $$1; share = $$2; zoneid = $3; printf("%Y - client=%s share=%s zone=%s)\n", walltimestamp, (all_clients) ? "all" : client, (all_shares) ? "all" : share, (all_zones) ? "all" : $$3); } smb:::op-*-start / ((all_clients) || (args[0]->ci_remote == client)) && ((all_shares) || (args[1]->soi_share == share)) && ((all_zones) || (args[1]->soi_zoneid == zoneid)) / { printf("clnt=%s mid=0x%x uid=0x%x tid=0x%x\n", args[0]->ci_remote, args[1]->soi_mid, args[1]->soi_uid, args[1]->soi_tid); } smb:::op-*-done / ((all_clients) || (args[0]->ci_remote == client)) && ((all_shares) || (args[1]->soi_share == share)) && ((all_zones) || (args[1]->soi_zoneid == zoneid)) / { printf("clnt=%s mid=0x%x status=0x%x\n", args[0]->ci_remote, args[1]->soi_mid, args[1]->soi_status); } dtrace:::END { } #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * Example using the "smb2" dtrace provider. * Traces all SMB commands. * * All these probes provide: * args[0] conninfo_t * args[1] smb2opinfo_t * Some also provide one of: (not used here) * args[2] smb_open_args_t * args[2] smb_rw_args_t * * Usage: smb2-trace.d [|all [|all] []]] * * example: smb2_trace.d 192.168.012.001 mypool_fs1 0 * * It is valid to specify or as "all" to * print data for all clients and/or all shares. * Omitting will print data for all zones. */ #pragma D option defaultargs dtrace:::BEGIN { all_clients = (($$1 == NULL) || ($$1 == "all")) ? 1 : 0; all_shares = (($$2 == NULL) || ($$2 == "all")) ? 1 : 0; all_zones = ($$3 == NULL) ? 1 : 0; client = $$1; share = $$2; zoneid = $3; printf("%Y - client=%s share=%s zone=%s)\n", walltimestamp, (all_clients) ? "all" : client, (all_shares) ? "all" : share, (all_zones) ? "all" : $$3); } smb2:::op-*-start / ((all_clients == 1) || (args[0]->ci_remote == client)) && ((all_shares == 1) || (args[1]->soi_share == share)) && ((all_zones == 1) || (args[1]->soi_zoneid == zoneid)) / { printf("clnt=%s mid=0x%x uid=0x%x tid=0x%x\n", args[0]->ci_remote, args[1]->soi_mid, args[1]->soi_uid, args[1]->soi_tid); } smb2:::op-*-done / ((all_clients == 1) || (args[0]->ci_remote == client)) && ((all_shares == 1) || (args[1]->soi_share == share)) && ((all_zones == 1) || (args[1]->soi_zoneid == zoneid)) / { printf("clnt=%s mid=0x%x status=0x%x\n", args[0]->ci_remote, args[1]->soi_mid, args[1]->soi_status); } dtrace:::END { } #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * User-level dtrace for smbd. Watch everything it does. * Usage: dtrace -s smbd-all.d -p `pgrep smbd` */ #pragma D option flowindent self int trace; self int mask; /* * Trace everything in smbd */ pid$target:*smbd::entry, pid$target:libmlsvc.so.1::entry, pid$target:libmlrpc.so.2::entry, pid$target:libsmbns.so.1::entry, pid$target:libsmb.so.1::entry, pid$target:libads.so.1::entry { self->trace++; } /* * If traced and not masked, print entry/return */ pid$target:*smbd::entry, pid$target:libmlsvc.so.1::entry, pid$target:libmlrpc.so.2::entry, pid$target:libsmbns.so.1::entry, pid$target:libsmb.so.1::entry, pid$target:libads.so.1::entry /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg0); printf("\t0x%x", arg1); printf("\t0x%x", arg2); printf("\t0x%x", arg3); printf("\t0x%x", arg4); printf("\t0x%x", arg5); } /* * Mask (don't print) all function calls below these functions. * These make many boring, repetitive function calls like * smb_mbtowc, smb_msgbuf_has_space, ... * * Also, libmlrpc has rather deep call stacks, particularly under * ndr_encode_decode_common(), so this stops traces below there. * Remove that from the mask actions to see the details. */ pid$target::ht_findfirst:entry, pid$target::ht_findnext:entry, pid$target::ndr_encode_decode_common:entry, pid$target::smb_msgbuf_decode:entry, pid$target::smb_msgbuf_encode:entry, pid$target::smb_strlwr:entry, pid$target::smb_strupr:entry, pid$target::smb_wcequiv_strlen:entry { self->mask++; } /* * Get some of the smbd debug messages, etc. */ pid$target:libsmb.so.1:smb_trace:entry /self->trace > 0 && self->mask == 0/ { printf("%s", copyinstr(arg0)); } pid$target:libsmb.so.1:smb_syslog:entry /self->trace > 0 && self->mask == 0/ { printf("%s", copyinstr(arg1)); } pid$target:libc_hwcap1.so.1:syslog:entry /self->trace > 0 && self->mask == 0/ { printf("%s", copyinstr(arg1)); } /* * Now inverses of above, unwind order. */ pid$target::ht_findfirst:return, pid$target::ht_findnext:return, pid$target::ndr_encode_decode_common:return, pid$target::smb_msgbuf_decode:return, pid$target::smb_msgbuf_encode:return, pid$target::smb_strlwr:return, pid$target::smb_strupr:return, pid$target::smb_wcequiv_strlen:return { self->mask--; } pid$target:*smbd::return, pid$target:libmlsvc.so.1::return, pid$target:libmlrpc.so.2::return, pid$target:libsmbns.so.1::return, pid$target:libsmb.so.1::return, pid$target:libads.so.1::return /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg1); } pid$target:*smbd::return, pid$target:libmlsvc.so.1::return, pid$target:libmlrpc.so.2::return, pid$target:libsmbns.so.1::return, pid$target:libsmb.so.1::return, pid$target:libads.so.1::return { self->trace--; } #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * User-level dtrace for the smbd authentication service * Usage: dtrace -s smbd-authsvc.d -p `pgrep smbd` */ #pragma D option flowindent self int trace; self int mask; /* * The smbd_authsvc_work() function is a good place to start tracing * to watch authentication. This function executes all the actions * associated with a single session setup conversation (even though * that conversation will usually involve multiple SMB requests). */ pid$target:*smbd:smbd_authsvc_work:entry { self->trace++; } /* * If traced and not masked, print entry/return */ pid$target:*smbd::entry, pid$target:libmlsvc.so.1::entry, pid$target:libmlrpc.so.2::entry, pid$target:libsmbns.so.1::entry, pid$target:libsmb.so.1::entry, pid$target:libsmbfs.so.1::entry /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg0); printf("\t0x%x", arg1); printf("\t0x%x", arg2); printf("\t0x%x", arg3); printf("\t0x%x", arg4); printf("\t0x%x", arg5); } /* * Mask (don't print) all function calls below these functions. * These make many boring, repetitive function calls like * smb_mbtowc, smb_msgbuf_has_space, ... * * Also, libmlrpc has rather deep call stacks, particularly under * ndr_encode_decode_common(), so this stops traces below there. * Remove that from the mask actions to see the details. */ pid$target::ndr_encode_decode_common:entry, pid$target::smb_msgbuf_decode:entry, pid$target::smb_msgbuf_encode:entry, pid$target::smb_strlwr:entry, pid$target::smb_strupr:entry, pid$target::smb_wcequiv_strlen:entry { self->mask++; } /* * Now inverses of above, unwind order. */ pid$target::ndr_encode_decode_common:return, pid$target::smb_msgbuf_decode:return, pid$target::smb_msgbuf_encode:return, pid$target::smb_strlwr:return, pid$target::smb_strupr:return, pid$target::smb_wcequiv_strlen:return { self->mask--; } pid$target:*smbd::return, pid$target:libmlsvc.so.1::return, pid$target:libmlrpc.so.2::return, pid$target:libsmbns.so.1::return, pid$target:libsmb.so.1::return, pid$target:libsmbfs.so.1::return /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg1); } pid$target:*smbd:smbd_authsvc_work:return { self->trace--; } #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * User-level dtrace for smbd * Usage: dtrace -s smbd-doorsvc.d -p `pgrep smbd` */ #pragma D option flowindent self int trace; self int mask; /* * smbd_door_dispatch_op() is the logical top of smbd door service calls. */ pid$target:*smbd:smbd_door_dispatch_op:entry { self->trace++; } /* * If traced and not masked, print entry/return */ pid$target:*smbd::entry, pid$target:libmlsvc.so.1::entry, pid$target:libmlrpc.so.2::entry, pid$target:libsmbns.so.1::entry, pid$target:libsmb.so.1::entry, pid$target:libsmbfs.so.1::entry /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg0); printf("\t0x%x", arg1); printf("\t0x%x", arg2); printf("\t0x%x", arg3); printf("\t0x%x", arg4); printf("\t0x%x", arg5); } /* * Mask (don't print) all function calls below these functions. * These make many boring, repetitive function calls like * smb_mbtowc, smb_msgbuf_has_space, ... */ pid$target::smb_msgbuf_decode:entry, pid$target::smb_msgbuf_encode:entry, pid$target::smb_strlwr:entry, pid$target::smb_strupr:entry, pid$target::smb_wcequiv_strlen:entry { self->mask++; } /* * Now inverses of above, unwind order. */ pid$target::smb_msgbuf_decode:return, pid$target::smb_msgbuf_encode:return, pid$target::smb_strlwr:return, pid$target::smb_strupr:return, pid$target::smb_wcequiv_strlen:return { self->mask--; } pid$target:*smbd::return, pid$target:libmlsvc.so.1::return, pid$target:libmlrpc.so.2::return, pid$target:libsmbns.so.1::return, pid$target:libsmb.so.1::return, pid$target:libsmbfs.so.1::return /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg1); } pid$target:*smbd:smbd_door_dispatch_op:return { self->trace--; } #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * User-level dtrace for smbd * Usage: dtrace -s smbd-pipesvc.d -p `pgrep smbd` */ #pragma D option flowindent self int trace; self int mask; /* * The pipesvc_worker() function is a good place to start tracing * to watch RPC service actions. This worker handles all activity * for a given named pipe instance, including the payload from all * SMB read/write requests on this endpoint. */ pid$target:*smbd:pipesvc_worker:entry { self->trace++; } /* * If traced and not masked, print entry/return */ pid$target:*smbd::entry, pid$target:libmlsvc.so.1::entry, pid$target:libmlrpc.so.2::entry, pid$target:libsmbns.so.1::entry, pid$target:libsmb.so.1::entry /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg0); printf("\t0x%x", arg1); printf("\t0x%x", arg2); printf("\t0x%x", arg3); printf("\t0x%x", arg4); printf("\t0x%x", arg5); } /* * Mask (don't print) all function calls below these functions. * These make many boring, repetitive function calls like * smb_mbtowc, smb_msgbuf_has_space, ... * * Also, libmlrpc has rather deep call stacks, particularly under * ndr_encode_decode_common(), so this stops traces below there. * Remove that from the mask actions to see the details. */ pid$target::ht_findfirst:entry, pid$target::ht_findnext:entry, pid$target::ndr_encode_decode_common:entry, pid$target::smb_msgbuf_decode:entry, pid$target::smb_msgbuf_encode:entry, pid$target::smb_strlwr:entry, pid$target::smb_strupr:entry, pid$target::smb_wcequiv_strlen:entry { self->mask++; } /* * Now inverses of above, unwind order. */ pid$target::ht_findfirst:return, pid$target::ht_findnext:return, pid$target::ndr_encode_decode_common:return, pid$target::smb_msgbuf_decode:return, pid$target::smb_msgbuf_encode:return, pid$target::smb_strlwr:return, pid$target::smb_strupr:return, pid$target::smb_wcequiv_strlen:return { self->mask--; } pid$target:*smbd::return, pid$target:libmlsvc.so.1::return, pid$target:libmlrpc.so.2::return, pid$target:libsmbns.so.1::return, pid$target:libsmb.so.1::return /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg1); } /* * This function in libmlrpc prints out lots of internal state. * Comment it out if you don't want that noise. */ pid$target:libmlrpc.so.2:ndo_trace:entry /self->trace > 0 && self->mask == 0/ { printf("ndo_trace: %s", copyinstr(arg0)); } pid$target:*smbd:pipesvc_worker:return { self->trace--; } #!/usr/sbin/dtrace -qs /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ BEGIN { printf("-->SMB Server Node Trace Started"); printf("\n\n"); } END { printf("<--SMB Server Node Trace Ended"); printf("\n\n"); } sdt:smbsrv:smb_node_lookup:smb_node_lookup_hit /((smb_node_t *)arg0)->n_state == SMB_NODE_STATE_AVAILABLE/ { printf("\nSMB Node lookup hit/SMB_NODE_STATE_AVAILABLE"); printf("\n\tNode: %p", arg0); printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); stack(); } sdt:smbsrv:smb_node_lookup:smb_node_lookup_hit /((smb_node_t *)arg0)->n_state == SMB_NODE_STATE_DESTROYING/ { printf("\nSMB Node lookup hit/SMB_NODE_STATE_DESTROYING"); printf("\n\tNode: %p", arg0); printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); stack(); } sdt:smbsrv:smb_node_lookup:smb_node_lookup_hit /(((smb_node_t *)arg0)->n_state != SMB_NODE_STATE_DESTROYING) && (((smb_node_t *)arg0)->n_state != SMB_NODE_STATE_AVAILABLE)/ { printf("\nSMB Node lookup hit/Unknown State"); printf("\n\tNode: %p", arg0); printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); stack(); } sdt:smbsrv:smb_node_lookup:smb_node_lookup_miss { printf("\nSMB Node lookup miss"); printf("\n\tNode: %p", arg0); printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); stack(); } sdt:smbsrv:smb_node_ref:smb_node_ref_exit { printf("\nSMB Node reference taken"); printf("\n\tNode: %p", arg0); printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); stack(); } sdt:smbsrv:smb_node_release:smb_node_release /((smb_node_t *)arg0)->n_refcnt == 1/ { printf("\nSMB Node release(will be destroyed)"); printf("\n\tNode: %p", arg0); printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); stack(); } sdt:smbsrv:smb_node_release:smb_node_release /((smb_node_t *)arg0)->n_refcnt > 1/ { printf("\nSMB Node release"); printf("\n\tNode: %p", arg0); printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); stack(); } sdt:smbsrv:smb_node_delete_on_close:smb_node_delete_on_close /(int)arg0 == 0/ { printf("\nSMB Node delete on close successful"); printf("\n\tNode: %p", arg1); printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg1)->vp)->v_path); stack(); } sdt:smbsrv:smb_node_delete_on_close:smb_node_delete_on_close /(int)arg0 == 0/ { printf("\nSMB Node delete on close failed (%d)", (int)arg0); printf("\n\tNode: %p", arg1); printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg1)->vp)->v_path); stack(); } #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * Developer dtrace program for smbsrv * Usage: dtrace -s smbsrv.d */ self int trace; self int mask; /* * Trace almost everything */ fbt:smbsrv::entry { self->trace++; } /* * If traced and not masked, print entry/return */ fbt:smbsrv::entry /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg0); printf("\t0x%x", arg1); printf("\t0x%x", arg2); printf("\t0x%x", arg3); printf("\t0x%x", arg4); printf("\t0x%x", arg5); } /* * Mask (don't print) all function calls below these functions. * These make many boring, repetitive function calls like * smb_mbtowc, mbc_marshal_... */ fbt::smb_mbc_vdecodef:entry, fbt::smb_mbc_vencodef:entry, fbt::smb_msgbuf_decode:entry, fbt::smb_msgbuf_encode:entry, fbt::smb_strlwr:entry, fbt::smb_strupr:entry, fbt::smb_wcequiv_strlen:entry { self->mask++; } /* * Now inverses of above, unwind order. */ fbt::smb_mbc_vdecodef:return, fbt::smb_mbc_vencodef:return, fbt::smb_msgbuf_decode:return, fbt::smb_msgbuf_encode:return, fbt::smb_strlwr:return, fbt::smb_strupr:return, fbt::smb_wcequiv_strlen:return { self->mask--; } fbt:smbsrv::return /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg1); } fbt:smbsrv::return { self->trace--; } /* * Use the "smb", "smb2" dtrace providers. */ smb:::op-*-start, smb2:::op-*-start { printf("clnt=%s mid=0x%x uid=0x%x tid=0x%x\n", args[0]->ci_remote, args[1]->soi_mid, args[1]->soi_uid, args[1]->soi_tid); } smb:::op-*-done, smb2:::op-*-done { printf("clnt=%s mid=0x%x status=0x%x\n", args[0]->ci_remote, args[1]->soi_mid, args[1]->soi_status); } #!/usr/sbin/dtrace -qs /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ BEGIN { printf("\n-->SMB Server VFS Trace Started"); printf("\n\n"); } END { printf("\n<--SMB Server VFS Trace Ended"); printf("\n\n"); } sdt:smbsrv:smb_vfs_hold:smb_vfs_hold_hit { printf("\nSMB VFS lookup hit"); printf("\n Path: %s", (string)((smb_vfs_t *)arg0)->sv_rootvp->v_path); printf("\n RefCount: %d", ((smb_vfs_t *)arg0)->sv_refcnt); } sdt:smbsrv:smb_vfs_hold:smb_vfs_hold_miss { printf("\nSMB VFS lookup miss"); printf("\n Path: %s", (string)((smb_vfs_t *)arg0)->sv_rootvp->v_path); printf("\n RefCount: %d", ((smb_vfs_t *)arg0)->sv_refcnt); } sdt:smbsrv:smb_vfs_rele:smb_vfs_release /(smb_vfs_t *)arg0 != 0/ { printf("\nSMB VFS release hit"); printf("\n Path: %s", (string)((smb_vfs_t *)arg0)->sv_rootvp->v_path); printf("\n RefCount: %d", ((smb_vfs_t *)arg0)->sv_refcnt - 2); } sdt:smbsrv:smb_vfs_rele:smb_vfs_release /(smb_vfs_t *)arg0 == 0/ { printf("\nSMB VFS release miss"); printf("\n Path: %s", (string)((vnode_t *)arg1)->v_path); } sdt:smbsrv:smb_vfs_rele_all:smb_vfs_rele_all_hit { printf("\nSMB VFS free"); printf("\n Path: %s", (string)((smb_vfs_t *)arg0)->sv_rootvp->v_path); printf("\n RefCount: %d", ((smb_vfs_t *)arg0)->sv_refcnt); } # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2013 Nexenta Systems, Inc. All rights reserved. # # set -o emacs setenv SMBD_DOOR_NAME /tmp/fksmbd_door setenv SMB_SHARE_DNAME /tmp/fksmbshare_door # fksmbd is always 32-bit setenv LD_LIBRARY_PATH ${ROOT}/usr/lib/smbsrv:${ROOT}/usr/lib:${ROOT}/lib echo 'Do one of: attach ${PID}' echo 'or: debug ${ROOT}/usr/lib/smbsrv/fksmbd' # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2021 Tintri by DDN, Inc. All rights reserved. # # gdb script (it allows python :) python import os gdb.execute('file' + os.environ['ROOT'] + '/usr/lib/smbsrv/fksmbd') gdb.execute('set env ' + 'LD_LIBRARY_PATH ' + \ os.environ['ROOT'] + '/lib:' + \ os.environ['ROOT'] + '/usr/lib') end echo break debug_enter\n # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2015 Nexenta Systems, Inc. All rights reserved. # PROG= fksmbd OBJS_SMBD= \ smbd_authsvc.o \ smbd_doorsvc.o \ smbd_join.o \ smbd_krb5lookup.o \ smbd_krb5ssp.o \ smbd_logon.o \ smbd_main.o \ smbd_nicmon.o \ smbd_ntlmssp.o \ smbd_pipesvc.o \ smbd_share_doorsvc.o \ smbd_spool.o \ smbd_vss.o \ OBJS_LOCAL = \ fksmbd_door.o \ fksmbd_kmod.o \ fksmbd_ksock.o \ fksmbd_log.o \ fksmbd_shr.o OBJS= ${OBJS_SMBD} ${OBJS_LOCAL} SRCS= ${OBJS_SMBD:%.o=../smbd/%.c} \ ${OBJS_LOCAL:.o=.c} include ../../Makefile.cmd include ../../Makefile.ctf # Hammerhead: Include mech_krb5 definitions for direct linking. # libkrb5.so is a filter library; GNU ld cannot resolve symbols through it. include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 # Note: need our sys includes _before_ ENVCPPFLAGS, proto etc. CPPFLAGS.first += -I../../../lib/smbsrv/libfksmbsrv/common INCS += -I../smbd INCS += -I../../../uts/common INCS += -I../../../uts/common/smbsrv INCS += -I../../../common/smbsrv # Should not have to do this, but the Kerberos includes are a mess. INCS += -I $(ROOT)/usr/include/kerberosv5 CSTD= $(CSTD_GNU99) CFLAGS += $(CCVERBOSE) CFLAGS64 += $(CCVERBOSE) # Hammerhead: smbd_krb5ssp.c uses pointer-to-int casts CERRWARN += -Wno-pointer-to-int-cast CPPFLAGS += -D_REENTRANT CPPFLAGS += -Dsyslog=smb_syslog CPPFLAGS += -D_LARGEFILE64_SOURCE=1 CPPFLAGS += -DFKSMBD # Always debug here CPPFLAGS += -DDEBUG CPPFLAGS += $(INCS) LDFLAGS += $(ZNOLAZYLOAD) LDFLAGS += '-R$$ORIGIN' '-R$$ORIGIN/..' LDLIBS += -L$(ROOT)/usr/lib/smbsrv LDLIBS += -lfksmbsrv -lfakekernel # prefer to keep libs ordered by dependence LDLIBS += -lmlsvc -lmlrpc -lsmbns -lsmb -lsmbfs -lgss LDLIBS += -lzfs -lbsm -lscf -lcmdutils -lsocket -lnsl -lumem # Hammerhead: Link directly against mech_krb5.so instead of filter lib -lkrb5 $(PROG) : LDLIBS += $(KMECHLIB) $(PROG) : LDFLAGS += $(KRUNPATH) -Wl,-rpath-link,$(ROOT_KLIBDIR) ROOTSMBDDIR = $(ROOTLIB)/smbsrv ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%) .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) $(POST_PROCESS) clean: -$(RM) $(OBJS) include ../../Makefile.targ install: all $(ROOTSMBDFILE) %.o: ../smbd/%.c $(COMPILE.c) $< $(POST_PROCESS_O) $(ROOTSMBDDIR)/%: % $(INS.file) # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2013 Nexenta Systems, Inc. All rights reserved. # This directory builds a program linking all of the SMB server code into a user-level process. The result is not a fully functional SMB server but is very useful for some kinds of development work. The architecture of this roughly parallels the in-kernel version, where the fksmbd program corresponds to the real smbd, the library libfksmbsrv corresponds to the smbsrv kernel module, and all the required kernel interfaces are simulated in libfakekernel. Just as with the kernel code, there are mdb modules that know how to walk data structures in libfksmbsrv, etc. For debugging, etc. it's easiest to run this as a normal user, i.e. yourself (not root); but before you can do that, there are some prerequisites to take care of: a: Install ../bind-helper in /usr/lib/smbsrv/. It needs to be either setuid root (easiest) or added to the exec_attr as described in the top of bind-helper.c (This must be in /usr/lib/smbsrv, not the proto area.) b: Setup an SMB account for guest, i.e. Add pam_smb_passwd.so.1 to pam.conf if not already there (other password required pam_smb_passwd.so.1 nowarn) % useradd guest % smbadm enable-user guest % passwd guest (sets the SMB p/w hash too) Make sure guest shows in /var/smb/smbpasswd c: chown/chmod the directories: /var/smb /var/run/smb to yourself, mode 755. Reboot will chown these back to root, so repeat this after a reboot. Now you can run fksmbd from the proto area using this script: ./Run.sh -df You can also run it under dbx (see the .dbxrc file). To run it under mdb (with mdb modules build here): mdb -L $ROOT/usr/lib/mdb/proc:/usr/lib/mdb/proc ... where ... is one of: fksmbd, core.nnn, -p $PID There are also some dtrace scripts in here, and in ../dtrace for watching either all activity or only selected areas. Run these like: dtrace -s Watch-all.d -p $PID -o output These two (from over in ../dtrace) also work with fksmbd: dtrace -s smbd-authsvc.d -p `pgrep fksmbd` -o output dtrace -s smbd-pipesvc.d -p `pgrep fksmbd` -o output Here are a couple simple, handy tests you can try: smbclient -L localhost -U guest%guest smbclient //localhost/test -U guest%guest -c dir smbtorture //localhost/test -U guest%guest base #!/bin/sh # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2013-2021 Tintri by DDN, Inc. All rights reserved. # # Helper program to run fksmbd (user-space smbd for debugging) # using binaries from the proto area. [ -n "$ROOT" ] || { echo "Need a bldenv to set ROOT=..." exit 1; } if [[ ! -w /var/smb || ! -w /var/run/smb ]] then echo "Need to create/chown/chmod /var/smb /var/run/smb" echo "mkdir -p /var/run/smb" echo "chown -R $USER /var/smb /var/run/smb" echo "chmod -R a+rw /var/smb /var/run/smb" exit 1; fi if [[ ! -r /var/smb/smbpasswd ]] then echo "Need readable /var/smb/smbpasswd, i.e." echo 'chgrp staff /var/smb/smbpasswd' echo 'chmod 440 /var/smb/smbpasswd' exit 1; fi if [[ -e /var/smb/.pwd.lock && ! -w /var/smb/.pwd.lock ]] then echo "Need to cleanup /var/smb/.pwd.lock, i.e." echo "rm -f /var/smb/.pwd.lock" exit 1; fi # OK, setup env. to run it. export SMBD_DOOR_NAME="/tmp/fksmbd_door" export SMB_SHARE_DNAME="/tmp/fksmbshare_door" export PATH_PKCS11_CONF=$ROOT/etc/crypto/pkcs11.conf LD_LIBRARY_PATH=$ROOT/usr/lib/smbsrv:$ROOT/usr/lib:$ROOT/usr/lib:$ROOT/lib export LD_LIBRARY_PATH # Enable everything, for debugging export SMB_MAX_PROTOCOL=311 export SMB_SIGNING=require # normally runs with cwd=/ but this is more careful cd /var/smb # run with the passed options exec $ROOT/usr/lib/smbsrv/fksmbd "$@" #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * User-level dtrace for fksmbd * Usage: dtrace -s Watch-all.d -p $PID */ self int trace; self int mask; /* * Trace almost everything */ pid$target:fksmbd::entry, pid$target:libfksmbsrv.so.1::entry, pid$target:libmlsvc.so.1::entry, pid$target:libmlrpc.so.2::entry, pid$target:libsmbns.so.1::entry, pid$target:libsmb.so.1::entry { self->trace++; } /* * If traced and not masked, print entry/return */ pid$target:fksmbd::entry, pid$target:libfksmbsrv.so.1::entry, pid$target:libmlsvc.so.1::entry, pid$target:libmlrpc.so.2::entry, pid$target:libsmbns.so.1::entry, pid$target:libsmb.so.1::entry /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg0); printf("\t0x%x", arg1); printf("\t0x%x", arg2); printf("\t0x%x", arg3); printf("\t0x%x", arg4); printf("\t0x%x", arg5); } /* * Mask (don't print) all function calls below these functions. * These make many boring, repetitive function calls like * smb_mbtowc, mbc_marshal_... */ pid$target::fop__getxvattr:entry, pid$target::fop__setxvattr:entry, pid$target::smb_mbc_vdecodef:entry, pid$target::smb_mbc_vencodef:entry, pid$target::smb_msgbuf_decode:entry, pid$target::smb_msgbuf_encode:entry, pid$target::smb_strlwr:entry, pid$target::smb_strupr:entry, pid$target::smb_wcequiv_strlen:entry { self->mask++; } /* * Now inverses of above, unwind order. */ pid$target::fop__getxvattr:return, pid$target::fop__setxvattr:return, pid$target::smb_mbc_vdecodef:return, pid$target::smb_mbc_vencodef:return, pid$target::smb_msgbuf_decode:return, pid$target::smb_msgbuf_encode:return, pid$target::smb_strlwr:return, pid$target::smb_strupr:return, pid$target::smb_wcequiv_strlen:return { self->mask--; } pid$target:fksmbd::return, pid$target:libfksmbsrv.so.1::return, pid$target:libmlsvc.so.1::return, pid$target:libmlrpc.so.2::return, pid$target:libsmbns.so.1::return, pid$target:libsmb.so.1::return /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg1); } pid$target:fksmbd::return, pid$target:libfksmbsrv.so.1::return, pid$target:libmlsvc.so.1::return, pid$target:libmlrpc.so.2::return, pid$target:libsmbns.so.1::return, pid$target:libsmb.so.1::return { self->trace--; } /* * fksmb dtrace provder */ fksmb$target:::smb_start { this->pn = copyinstr(arg0); this->sr = (userland pid`smb_request_t *)arg1; printf(" %s mid=0x%x uid=0x%x tid=0x%x\n", this->pn, this->sr->smb_mid, this->sr->smb_uid, this->sr->smb_tid); } fksmb$target:::smb_done { this->pn = copyinstr(arg0); this->sr = (userland pid`smb_request_t *)arg1; printf(" %s mid=0x%x status=0x%x\n", this->pn, this->sr->smb_mid, this->sr->smb_error.status); } fksmb$target:::smb2_start { this->pn = copyinstr(arg0); this->sr = (userland pid`smb_request_t *)arg1; printf(" %s mid=0x%x uid=0x%x tid=0x%x\n", this->pn, this->sr->smb2_messageid, this->sr->smb2_ssnid, this->sr->smb_tid); } fksmb$target:::smb2_done { this->pn = copyinstr(arg0); this->sr = (userland pid`smb_request_t *)arg1; printf(" %s mid=0x%x status=0x%x\n", this->pn, this->sr->smb2_messageid, this->sr->smb2_status); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * Example using the "fksmb$pid" dtrace provider. * Traces all SMB commands using the probes: * start, done * all of which have two args: * args[0]: char * (probe-name) * args[1]: ulong (struct smb_request *) * * Note: the "userland" type classifier causes dtrace to * automatically copyin the struct for us. (Nice!) */ fksmb$target:::smb_start { this->pn = copyinstr(arg0); this->sr = (userland pid`smb_request_t *)arg1; printf(" %s mid=0x%x uid=0x%x tid=0x%x\n", this->pn, this->sr->smb_mid, this->sr->smb_uid, this->sr->smb_tid); } fksmb$target:::smb_done { this->pn = copyinstr(arg0); this->sr = (userland pid`smb_request_t *)arg1; printf(" %s mid=0x%x status=0x%x\n", this->pn, this->sr->smb_mid, this->sr->smb_error.status); } fksmb$target:::smb2_start { this->pn = copyinstr(arg0); this->sr = (userland pid`smb_request_t *)arg1; printf(" %s mid=0x%x uid=0x%x tid=0x%x\n", this->pn, this->sr->smb2_messageid, this->sr->smb2_ssnid, this->sr->smb_tid); } fksmb$target:::smb2_done { this->pn = copyinstr(arg0); this->sr = (userland pid`smb_request_t *)arg1; printf(" %s mid=0x%x status=0x%x\n", this->pn, this->sr->smb2_messageid, this->sr->smb2_status); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" /* * Special version of smb_door_dispatch() for the * "fake" smbsrv (running in user space). * * This is called via function pointer from * smbsrv: smb_kdoor_upcall() * * The args and response go RPC encoded, just so we can * borrow some of the common doorsvc code, even though * there's no need for RPC encoding in this scenario. */ int fksmbd_door_dispatch(smb_doorarg_t *da) { smbd_arg_t dop_arg; smb_doorhdr_t *hdr; char *rbuf = NULL; char *argp = da->da_arg.data_ptr; size_t arg_size = da->da_arg.data_size; size_t hdr_size, rsize; /* * Decode * * da->da_arg.data_ptr = (arg data, xdr encoded) * da->da_arg.data_size = (arg data len) */ bzero(&dop_arg, sizeof (smbd_arg_t)); hdr = &dop_arg.hdr; hdr_size = xdr_sizeof(smb_doorhdr_xdr, hdr); if ((argp == NULL) || (arg_size < hdr_size)) { syslog(LOG_DEBUG, "fksmbd_door_dispatch: bad args"); return (-1); } if (smb_doorhdr_decode(hdr, (uint8_t *)argp, hdr_size) == -1) { syslog(LOG_DEBUG, "smbd_door_dispatch: header decode failed"); return (-1); } if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) || (hdr->dh_flags != SMB_DF_FAKE_KERNEL)) { syslog(LOG_DEBUG, "fksmbd_door_dispatch: invalid header"); return (-1); } dop_arg.opname = smb_doorhdr_opname(hdr->dh_op); dop_arg.data = argp + hdr_size; dop_arg.datalen = hdr->dh_datalen; if (hdr->dh_op == SMB_DR_ASYNC_RESPONSE) { /* * ASYNC_RESPONSE is not used here. */ syslog(LOG_DEBUG, "fksmbd_door_dispatch: ASYNC?"); return (-1); } /* * Dispatch * * Call the common smbd_doorsvc.c code. */ (void) smbd_door_dispatch_op(&dop_arg); /* * Encode * * da->da_arg.rbuf = (return data buf) * da->da_arg.rsize = (return data size) * * Note that the return data buffer initially * points to the same buffer as the args. * If that's not large enough, umem_alloc. */ rsize = dop_arg.rsize + hdr_size; rbuf = umem_alloc(rsize, UMEM_DEFAULT); if (rbuf == NULL) { syslog(LOG_DEBUG, "fksmbd_door_dispatch[%s]: alloc %m", dop_arg.opname); return (-1); } /* Copy caller's return data after the header. */ if (dop_arg.rbuf != NULL) { (void) memcpy(rbuf + hdr_size, dop_arg.rbuf, dop_arg.rsize); free(dop_arg.rbuf); } hdr->dh_datalen = dop_arg.rsize; (void) smb_doorhdr_encode(hdr, (uint8_t *)rbuf, hdr_size); /* Let's update da->da_hdr too. */ da->da_hdr = *hdr; /* * Was door_return() * NB: The "fake kernel" smbsrv code will umem_free rbuf. */ da->da_arg.rbuf = rbuf; da->da_arg.rsize = rsize; return (0); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * These replace NODIRECT functions of the same name in * $SRC/lib/smbsrv/libsmb/common/smb_kmod.c including: * smb_kmod_bind, smb_kmod_ioctl, smb_kmod_isbound, * smb_kmod_start, smb_kmod_stop, smb_kmod_unbind. * * For all the other smb_kmod_... functions, we can just use the * libsmb code because those all call smb_kmod_ioctl, for which * we have an override here. * * The replacment functions here just call the libfksmbsrv code * directly where the real (in-kernel) versions would be entered * via the driver framework (open, close, ioctl). Aside from that, * the call sequences are intentionally the same (where possible). * In particular, that makes it possible to debug startup/teardown * problems in the user-space version of this code. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" boolean_t smbdrv_opened = B_FALSE; /* * We want to adjust a few things in the standard configuration * passed to the "fake" version of the smbsrv kernel module. * * Reduce the maximum number of connections and workers, just for * convenience while debugging. (Don't want hundreds of threads.) */ static void fksmbd_adjust_config(smb_ioc_header_t *ioc_hdr) { smb_ioc_cfg_t *ioc = (smb_ioc_cfg_t *)ioc_hdr; char *s; ioc->maxconnections = 10; ioc->maxworkers = 20; smbd_report("maxconnections=%d, maxworkers=%d", ioc->maxconnections, ioc->maxworkers); if ((s = getenv("SMB_MAX_PROTOCOL")) != NULL) { ioc->max_protocol = strtol(s, NULL, 16); smbd_report("max_protocol=0x%x", ioc->max_protocol); } if ((s = getenv("SMB_MIN_PROTOCOL")) != NULL) { ioc->min_protocol = strtol(s, NULL, 16); smbd_report("min_protocol=0x%x", ioc->min_protocol); } if ((s = getenv("SMB_SIGNING")) != NULL) { ioc->signing_enable = 0; ioc->signing_required = 0; switch (s[0]) { case 'e': ioc->signing_enable = 1; break; case 'r': ioc->signing_enable = 1; ioc->signing_required = 1; break; default: smbd_report("env SMB_SIGNING invalid"); break; } } smbd_report("signing: enable=%d, required=%d", ioc->signing_enable, ioc->signing_required); } boolean_t smb_kmod_isbound(void) { return (smbdrv_opened); } int smb_kmod_bind(boolean_t smbd) { int rc; if (!smbd) return (ENXIO); if (smbdrv_opened) { smbdrv_opened = B_FALSE; (void) fksmbsrv_drv_close(); } rc = fksmbsrv_drv_open(); if (rc == 0) smbdrv_opened = B_TRUE; return (rc); } void smb_kmod_unbind(void) { if (smbdrv_opened) { smbdrv_opened = B_FALSE; (void) fksmbsrv_drv_close(); } } int smb_kmod_ioctl(int cmd, smb_ioc_header_t *ioc, uint32_t len) { int rc; _NOTE(ARGUNUSED(len)); if (!smbdrv_opened) return (EBADF); if (cmd == SMB_IOC_CONFIG) fksmbd_adjust_config(ioc); rc = fksmbsrv_drv_ioctl(cmd, ioc); return (rc); } /* ARGSUSED */ int smb_kmod_start(int opipe, int lmshr, int udoor) { smb_ioc_start_t ioc; int rc; bzero(&ioc, sizeof (ioc)); /* These three are unused */ ioc.opipe = -1; ioc.lmshrd = -1; ioc.udoor = -1; /* These are the "door" dispatch callbacks */ ioc.lmshr_func = NULL; /* not used */ ioc.opipe_func = NULL; /* not used */ ioc.udoor_func = (void *)fksmbd_door_dispatch; rc = smb_kmod_ioctl(SMB_IOC_START, &ioc.hdr, sizeof (ioc)); return (rc); } void smb_kmod_stop(void) { smb_ioc_header_t ioc; bzero(&ioc, sizeof (ioc)); (void) smb_kmod_ioctl(SMB_IOC_STOP, &ioc, sizeof (ioc)); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * fork/exec a privileged helper to do the bind. */ #include #include #include #include #include #include #include #include #include #include int ksocket_bind_helper(int fd, struct sockaddr *addr, uint_t addrlen) { char familystr[8]; char portstr[12]; char addrstr[INET6_ADDRSTRLEN]; char *argv[6]; const char *p; /* LINTED E_BAD_PTR_CAST_ALIGN */ struct sockaddr_in *sin = (struct sockaddr_in *)addr; /* LINTED E_BAD_PTR_CAST_ALIGN */ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; int pid, err, stat; _NOTE(ARGUNUSED(addrlen)); (void) snprintf(familystr, sizeof (familystr), "%d", addr->sa_family); switch (addr->sa_family) { case AF_INET: (void) snprintf(portstr, sizeof (portstr), "%d", ntohs(sin->sin_port)); p = inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof (addrstr)); break; case AF_INET6: (void) snprintf(portstr, sizeof (portstr), "%d", ntohs(sin6->sin6_port)); p = inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, sizeof (addrstr)); break; default: p = NULL; break; } if (p == NULL) { err = errno; (void) fprintf(stdout, "ksocket_bind_helper, inet_ntop %s\n", strerror(err)); return (err); } (void) fprintf(stdout, "ksocket_bind_helper, " "family=%s addr=%s port=%s\n", familystr, addrstr, portstr); argv[0] = "/usr/bin/pfexec"; argv[1] = "/usr/lib/smbsrv/bind-helper"; argv[2] = familystr; argv[3] = addrstr; argv[4] = portstr; argv[5] = NULL; pid = vfork(); if (pid == -1) { err = errno; perror("fork"); return (err); } if (pid == 0) { (void) dup2(fd, 0); (void) execv(argv[0], argv); err = errno; perror("execv"); return (err); } err = waitpid(pid, &stat, 0); if (err == -1) { err = errno; perror("waitpid"); return (err); } if (WIFEXITED(stat)) { err = WEXITSTATUS(stat); if (err == 0) return (0); (void) fprintf(stderr, "helper exit %d\n", err); } return (EACCES); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include "smbd.h" #include static const char *pri_name[LOG_DEBUG+1] = { "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" }; /* * Provide a replacement for libsmb:smb_vsyslog() that just * prints the messages to stdout for "fksmbd" debugging. */ void smb_vsyslog(int pri, const char *fmt, va_list ap) { int save_errno = errno; char buf[SMBD_LOG_MSGSIZE]; char *newfmt; pri &= LOG_PRIMASK; if (smbd.s_debug == 0 && pri > LOG_INFO) return; newfmt = smb_syslog_fmt_m(buf, sizeof (buf), fmt, save_errno); flockfile(stdout); (void) fprintf(stdout, "fksmbd.%s: ", pri_name[pri]); /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) vfprintf(stdout, newfmt, ap); (void) fprintf(stdout, "\n"); funlockfile(stdout); (void) fflush(stdout); } /* * Provide a real function (one that prints something) to replace * the stub in libfakekernel. This prints cmn_err() messages. */ void fakekernel_putlog(char *msg, size_t len, int flags) { /* * [CE_CONT, CE_NOTE, CE_WARN, CE_PANIC] maps to * [SL_NOTE, SL_NOTE, SL_WARN, SL_FATAL] */ if (smbd.s_debug == 0 && (flags & SL_NOTE)) return; (void) fwrite(msg, 1, len, stdout); (void) fflush(stdout); } /* * Initialization function called at the start of fksmbd:main(). * Call an empty function in both of libfksmbsrv, libfakekernel, * just to force them to load so we can set breakpoints in them * without debugger forceload tricks. This also avoids elfchk * complaints from libfakekernel, which we don't call directly * except for here. */ void fksmbd_init(void) { fksmbsrv_drv_load(); fakekernel_init(); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2014-2021 Tintri by DDN, Inc. All rights reserved. */ /* * Replace the smb_shr_load() function in libmlsvc, because * fksmbd doesn't want the real shares known by libshare, * instead preferring its own (fake) list of shares. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SMB_SHRF_ENCRYPT 0x8000 // SMB2_SHAREFLAG_ENCRYPT_DATA? static void new_share(char *name, char *path, char *comment, int flags) { smb_share_t si; bzero(&si, sizeof (si)); (void) strlcpy(si.shr_name, name, MAXNAMELEN); (void) strlcpy(si.shr_path, path, MAXPATHLEN); (void) strlcpy(si.shr_cmnt, comment, SMB_SHARE_CMNT_MAX); if (flags & SMB_SHRF_ENCRYPT) { flags &= ~SMB_SHRF_ENCRYPT; si.shr_encrypt = SMB_CONFIG_REQUIRED; } si.shr_flags = flags; if (smb_shr_add(&si) != 0) { syslog(LOG_ERR, "failed to add test share: %s", si.shr_name); } } /* * This function loads a list of shares from a text file, where * each line of the file contains: * name path comment * * This is only for fksmbd, for testing. */ void shr_load_file(char *shr_file) { char linebuf[1024]; FILE *fp; char *p; char *name, *path, *comment; fp = fopen(shr_file, "r"); if (fp == NULL) { perror(shr_file); return; } while ((p = fgets(linebuf, sizeof (linebuf), fp)) != NULL) { name = p; p = strpbrk(p, " \t\n"); if (p == NULL) continue; *p++ = '\0'; path = p; p = strpbrk(p, " \t\n"); if (p == NULL) comment = ""; else { *p++ = '\0'; comment = p; p = strchr(p, '\n'); if (p != NULL) *p++ = '\0'; } new_share(name, path, comment, 0); } (void) fclose(fp); } /*ARGSUSED*/ void * smb_shr_load(void *args) { char *shr_file; _NOTE(ARGUNUSED(args)) /* * Not loading the real shares in fksmbd because that * tries to enable the network/smb/server service. * Also, we won't generally have access to everything * in the real shares, because fksmbd runs (only) with * the credentials of the user who runs it. */ new_share("test", "/var/smb/test", "fksmbd test share", SMB_SHRF_GUEST_OK); new_share("testca", "/var/smb/test", "fksmbd test CA share", SMB_SHRF_CA); new_share("teste", "/var/smb/test", "fksmbd test encrypted share", SMB_SHRF_ENCRYPT); /* Allow creating lots of shares for testing. */ shr_file = getenv("FKSMBD_SHARE_FILE"); if (shr_file != NULL) shr_load_file(shr_file); return (NULL); } void smb_shr_load_execinfo() { } void smb_shr_unload() { } # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # PROG= nvlprint include ../../Makefile.cmd ROOTCMDDIR= $(ROOT)/usr/lib/smbsrv CFLAGS += $(CCVERBOSE) CPPFLAGS += -D_FILE_OFFSET_BITS=64 LDLIBS += -lnvpair .KEEP_STATE: all: $(PROG) install: all $(ROOTCMD) clean: lint: include ../../Makefile.targ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * Print a packed nvlist from a file. */ #include #include #include #include #include "libnvpair.h" char buf[65536]; void dumpit(FILE *fp) { struct stat st; size_t flen; int rlen; nvlist_t *nvl = NULL; int err; if (fstat(fileno(fp), &st) < 0) { perror("fstat"); return; } flen = (size_t)st.st_size; if (flen > sizeof (buf)) { (void) printf("File too large\n"); return; } rlen = fread(buf, 1, flen, fp); if (rlen <= 0) { perror("fread"); return; } if (rlen != flen) { (void) printf("Short read %d %d \n", rlen, flen); return; } err = nvlist_unpack(buf, flen, &nvl, 0); if (err != 0) { (void) printf("nvlist_unpack, err=%d\n", err); return; } nvlist_print(stdout, nvl); nvlist_free(nvl); } int main(int argc, char **argv) { FILE *fp; int i; if (argc < 2) { (void) fprintf(stderr, "usage: %s {filename} [filename2...]\n", argv[0]); return (1); } for (i = 1; i < argc; i++) { fp = fopen(argv[i], "r"); if (fp == NULL) { perror(argv[i]); return (1); } (void) printf("%s:\n", argv[i]); dumpit(fp); (void) fclose(fp); } return (0); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2019 Nexenta by DDN, Inc. All rights reserved. # Copyright (c) 2018, Joyent, Inc. # PROG= smbadm SRCS= smbadm.c include ../../Makefile.cmd include ../../Makefile.ctf include ../Makefile.smbsrv.defs OBJS= smbadm.o smbinfo.o LDLIBS += -L$(ROOT)/usr/lib/smbsrv -lsmb -lsmbns \ -lsecdb -lofmt -lcmdutils -lumem LDFLAGS += -R/usr/lib/smbsrv .KEEP_STATE: SMOFF += free all: $(PROG) clean: $(RM) $(OBJS) lint: lint_SRCS $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) include ../../Makefile.targ install: all .WAIT $(ROOTUSRSBINPROG) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. * Copyright 2022-2023 RackTop Systems, Inc. */ /* * This module contains smbadm CLI which offers smb configuration * functionalities. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbadm.h" #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif typedef enum { HELP_ADD_MEMBER, HELP_CREATE, HELP_DELETE, HELP_DEL_MEMBER, HELP_GET, HELP_JOIN, HELP_LIST, HELP_LIST_DOMAINS, HELP_LIST_SESS, HELP_LIST_TREES, HELP_LIST_OFILES, HELP_CLOSE_SESS, HELP_CLOSE_OFILE, HELP_LOOKUP, HELP_RENAME, HELP_SET, HELP_SHOW, HELP_USER_DISABLE, HELP_USER_ENABLE, HELP_USER_DELETE } smbadm_help_t; #define SMBADM_CMDF_NONE 0x00 #define SMBADM_CMDF_USER 0x01 /* needs smb_pwd_init */ #define SMBADM_CMDF_GROUP 0x02 /* needs smb_lgrp_start */ #define SMBADM_CMDF_KMOD 0x04 /* needs smb_kmod_bind */ #define SMBADM_CMDF_TYPEMASK 0x0F typedef enum { SMBADM_GRP_ADDMEMBER = 0, SMBADM_GRP_DELMEMBER, } smbadm_grp_action_t; #define SMBADM_ANSBUFSIZ 64 typedef struct smbadm_cmdinfo { char *name; int (*func)(int, char **); smbadm_help_t usage; uint32_t flags; char *auth; } smbadm_cmdinfo_t; smbadm_cmdinfo_t *curcmd; static char *progname; #define SMBADM_ACTION_AUTH "solaris.smf.manage.smb" #define SMBADM_VALUE_AUTH "solaris.smf.value.smb" #define SMBADM_BASIC_AUTH "solaris.network.hosts.read" static boolean_t smbadm_checkauth(const char *); static void smbadm_usage(boolean_t) __NORETURN; static int smbadm_join_workgroup(const char *, boolean_t); static int smbadm_join_domain(const char *, const char *, const char *, boolean_t); static void smbadm_extract_domain(char *, char **, char **); static void smbadm_update_groups(smbadm_grp_action_t); static int smbadm_join(int, char **); static int smbadm_list(int, char **); static int smbadm_lookup(int, char **); static void smbadm_lookup_name(char *, boolean_t); static void smbadm_lookup_sid(char *, boolean_t); static int smbadm_group_create(int, char **); static int smbadm_group_delete(int, char **); static int smbadm_group_rename(int, char **); static int smbadm_group_show(int, char **); static void smbadm_group_show_name(const char *, const char *); static int smbadm_group_getprop(int, char **); static int smbadm_group_setprop(int, char **); static int smbadm_group_addmember(int, char **); static int smbadm_group_delmember(int, char **); static int smbadm_group_add_del_member(char *, char *, smbadm_grp_action_t); static int smbadm_user_delete(int, char **); static int smbadm_user_disable(int, char **); static int smbadm_user_enable(int, char **); /* Please keep the order consistent with smbadm(8) man page */ static smbadm_cmdinfo_t smbadm_cmdtable[] = { { "create", smbadm_group_create, HELP_CREATE, SMBADM_CMDF_GROUP, SMBADM_ACTION_AUTH }, { "delete", smbadm_group_delete, HELP_DELETE, SMBADM_CMDF_GROUP, SMBADM_ACTION_AUTH }, { "rename", smbadm_group_rename, HELP_RENAME, SMBADM_CMDF_GROUP, SMBADM_ACTION_AUTH }, { "show", smbadm_group_show, HELP_SHOW, SMBADM_CMDF_GROUP, SMBADM_ACTION_AUTH }, { "get", smbadm_group_getprop, HELP_GET, SMBADM_CMDF_GROUP, SMBADM_ACTION_AUTH }, { "set", smbadm_group_setprop, HELP_SET, SMBADM_CMDF_GROUP, SMBADM_ACTION_AUTH }, { "add-member", smbadm_group_addmember, HELP_ADD_MEMBER, SMBADM_CMDF_GROUP, SMBADM_ACTION_AUTH }, { "remove-member", smbadm_group_delmember, HELP_DEL_MEMBER, SMBADM_CMDF_GROUP, SMBADM_ACTION_AUTH }, { "delete-user", smbadm_user_delete, HELP_USER_DELETE, SMBADM_CMDF_USER, SMBADM_ACTION_AUTH }, { "disable-user", smbadm_user_disable, HELP_USER_DISABLE, SMBADM_CMDF_USER, SMBADM_ACTION_AUTH }, { "enable-user", smbadm_user_enable, HELP_USER_ENABLE, SMBADM_CMDF_USER, SMBADM_ACTION_AUTH }, { "join", smbadm_join, HELP_JOIN, SMBADM_CMDF_GROUP, SMBADM_VALUE_AUTH }, /* "list" is now an alias for "list-domains" */ { "list", smbadm_list, HELP_LIST, SMBADM_CMDF_NONE, SMBADM_BASIC_AUTH }, { "list-domains", smbadm_list, HELP_LIST_DOMAINS, SMBADM_CMDF_NONE, SMBADM_BASIC_AUTH }, { "list-sessions", cmd_list_sess, HELP_LIST_SESS, SMBADM_CMDF_KMOD, SMBADM_BASIC_AUTH }, { "list-trees", cmd_list_trees, HELP_LIST_TREES, SMBADM_CMDF_KMOD, SMBADM_BASIC_AUTH }, { "list-ofiles", cmd_list_ofiles, HELP_LIST_OFILES, SMBADM_CMDF_KMOD, SMBADM_BASIC_AUTH }, { "close-session", cmd_close_sess, HELP_CLOSE_SESS, SMBADM_CMDF_KMOD, SMBADM_BASIC_AUTH }, { "close-ofile", cmd_close_ofile, HELP_CLOSE_OFILE, SMBADM_CMDF_KMOD, SMBADM_BASIC_AUTH }, { "lookup", smbadm_lookup, HELP_LOOKUP, SMBADM_CMDF_NONE, SMBADM_BASIC_AUTH }, }; #define SMBADM_NCMD (sizeof (smbadm_cmdtable) / sizeof (smbadm_cmdtable[0])) typedef struct smbadm_prop { char *p_name; char *p_value; } smbadm_prop_t; typedef struct smbadm_prop_handle { char *p_name; char *p_dispvalue; int (*p_setfn)(char *, smbadm_prop_t *); int (*p_getfn)(char *, smbadm_prop_t *); boolean_t (*p_chkfn)(smbadm_prop_t *); } smbadm_prop_handle_t; static boolean_t smbadm_prop_validate(smbadm_prop_t *prop, boolean_t chkval); static int smbadm_prop_parse(char *arg, smbadm_prop_t *prop); static smbadm_prop_handle_t *smbadm_prop_gethandle(char *pname); static boolean_t smbadm_chkprop_priv(smbadm_prop_t *prop); static int smbadm_setprop_tkowner(char *gname, smbadm_prop_t *prop); static int smbadm_getprop_tkowner(char *gname, smbadm_prop_t *prop); static int smbadm_setprop_readfile(char *gname, smbadm_prop_t *prop); static int smbadm_getprop_readfile(char *gname, smbadm_prop_t *prop); static int smbadm_setprop_writefile(char *gname, smbadm_prop_t *prop); static int smbadm_getprop_writefile(char *gname, smbadm_prop_t *prop); static int smbadm_setprop_backup(char *gname, smbadm_prop_t *prop); static int smbadm_getprop_backup(char *gname, smbadm_prop_t *prop); static int smbadm_setprop_restore(char *gname, smbadm_prop_t *prop); static int smbadm_getprop_restore(char *gname, smbadm_prop_t *prop); static int smbadm_setprop_desc(char *gname, smbadm_prop_t *prop); static int smbadm_getprop_desc(char *gname, smbadm_prop_t *prop); static smbadm_prop_handle_t smbadm_ptable[] = { {"backup", "on|off", smbadm_setprop_backup, smbadm_getprop_backup, smbadm_chkprop_priv }, {"restore", "on|off", smbadm_setprop_restore, smbadm_getprop_restore, smbadm_chkprop_priv }, {"take-ownership", "on|off", smbadm_setprop_tkowner, smbadm_getprop_tkowner, smbadm_chkprop_priv }, {"bypass-read", "on|off", smbadm_setprop_readfile, smbadm_getprop_readfile, smbadm_chkprop_priv }, {"bypass-write", "on|off", smbadm_setprop_writefile, smbadm_getprop_writefile, smbadm_chkprop_priv }, {"description", "", smbadm_setprop_desc, smbadm_getprop_desc, NULL }, }; static int smbadm_init(void); static void smbadm_fini(void); static const char *smbadm_pwd_strerror(int error); /* * Number of supported properties */ #define SMBADM_NPROP (sizeof (smbadm_ptable) / sizeof (smbadm_ptable[0])) static void smbadm_cmdusage(FILE *fp, smbadm_cmdinfo_t *cmd) { switch (cmd->usage) { case HELP_ADD_MEMBER: (void) fprintf(fp, gettext("\t%s -m [-m ]... \n"), cmd->name); return; case HELP_CREATE: (void) fprintf(fp, gettext("\t%s [-d ] \n"), cmd->name); return; case HELP_DELETE: (void) fprintf(fp, gettext("\t%s \n"), cmd->name); return; case HELP_USER_DELETE: case HELP_USER_DISABLE: case HELP_USER_ENABLE: (void) fprintf(fp, gettext("\t%s \n"), cmd->name); return; case HELP_GET: (void) fprintf(fp, gettext("\t%s [-p ]... \n"), cmd->name); return; case HELP_JOIN: #if 0 /* Don't document "-p" yet, still needs work (NEX-11960) */ (void) fprintf(fp, gettext("\t%s [-y] -p \n" "\t%s [-y] [-c container] -u \n" "\t%s [-y] -w \n"), cmd->name, cmd->name, cmd->name); #else (void) fprintf(fp, gettext( "\t%s [-y] [-c container] -u \n" "\t%s [-y] -w \n"), cmd->name, cmd->name); #endif return; case HELP_LIST: (void) fprintf(fp, gettext( "\t%s (alias for list-domains)\n"), cmd->name); return; case HELP_LIST_DOMAINS: (void) fprintf(fp, gettext("\t%s\n"), cmd->name); return; case HELP_LIST_SESS: case HELP_LIST_TREES: case HELP_LIST_OFILES: (void) fprintf(fp, gettext( "\t%s [-p] [-o field,...]\n"), cmd->name); return; case HELP_CLOSE_SESS: (void) fprintf(fp, gettext( "\t%s [user_name]\n"), cmd->name); return; case HELP_CLOSE_OFILE: (void) fprintf(fp, gettext("\t%s \n"), cmd->name); return; case HELP_LOOKUP: (void) fprintf(fp, gettext("\t%s [-p] \n"), cmd->name); return; case HELP_DEL_MEMBER: (void) fprintf(fp, gettext("\t%s -m [-m ]... \n"), cmd->name); return; case HELP_RENAME: (void) fprintf(fp, gettext("\t%s \n"), cmd->name); return; case HELP_SET: (void) fprintf(fp, gettext("\t%s -p = " "[-p =]... \n"), cmd->name); return; case HELP_SHOW: (void) fprintf(fp, gettext("\t%s [-mps] []\n"), cmd->name); return; default: break; } abort(); /* NOTREACHED */ } static void smbadm_usage(boolean_t requested) { FILE *fp = requested ? stdout : stderr; boolean_t show_props = B_FALSE; int i; if (curcmd == NULL) { (void) fprintf(fp, gettext("usage: %s ...\n"), progname); for (i = 0; i < SMBADM_NCMD; i++) smbadm_cmdusage(fp, &smbadm_cmdtable[i]); (void) fprintf(fp, gettext("\nFor property list, run %s %s|%s\n"), progname, "get", "set"); exit(requested ? 0 : 2); } (void) fprintf(fp, gettext("usage:\n")); smbadm_cmdusage(fp, curcmd); if (strcmp(curcmd->name, "get") == 0 || strcmp(curcmd->name, "set") == 0) show_props = B_TRUE; if (show_props) { (void) fprintf(fp, gettext("\nThe following properties are supported:\n")); (void) fprintf(fp, "\n\t%-16s %s\n\n", "PROPERTY", "VALUES"); for (i = 0; i < SMBADM_NPROP; i++) { (void) fprintf(fp, "\t%-16s %s\n", smbadm_ptable[i].p_name, smbadm_ptable[i].p_dispvalue); } } exit(requested ? 0 : 2); } /* * smbadm_strcasecmplist * * Find a string 's' within a list of strings. * * Returns the index of the matching string or -1 if there is no match. */ static int smbadm_strcasecmplist(const char *s, ...) { va_list ap; char *p; int ndx; va_start(ap, s); for (ndx = 0; ((p = va_arg(ap, char *)) != NULL); ++ndx) { if (strcasecmp(s, p) == 0) { va_end(ap); return (ndx); } } va_end(ap); return (-1); } /* * smbadm_answer_prompt * * Prompt for the answer to a question. A default response must be * specified, which will be used if the user presses without * answering the question. */ static int smbadm_answer_prompt(const char *prompt, char *answer, const char *dflt) { char buf[SMBADM_ANSBUFSIZ]; char *p; (void) printf(gettext("%s [%s]: "), prompt, dflt); if (fgets(buf, SMBADM_ANSBUFSIZ, stdin) == NULL) return (-1); if ((p = strchr(buf, '\n')) != NULL) *p = '\0'; if (*buf == '\0') (void) strlcpy(answer, dflt, SMBADM_ANSBUFSIZ); else (void) strlcpy(answer, buf, SMBADM_ANSBUFSIZ); return (0); } /* * smbadm_confirm * * Ask a question that requires a yes/no answer. * A default response must be specified. */ static boolean_t smbadm_confirm(const char *prompt, const char *dflt) { char buf[SMBADM_ANSBUFSIZ]; for (;;) { if (smbadm_answer_prompt(prompt, buf, dflt) < 0) return (B_FALSE); if (smbadm_strcasecmplist(buf, "n", "no", 0) >= 0) return (B_FALSE); if (smbadm_strcasecmplist(buf, "y", "yes", 0) >= 0) return (B_TRUE); (void) printf(gettext("Please answer yes or no.\n")); } } static boolean_t smbadm_join_confirm(const char *domain) { (void) printf(gettext("After joining %s the smb service will be " "restarted automatically.\n"), domain); return (smbadm_confirm("Would you like to continue?", "no")); } static void smbadm_restart_service(void) { if (smb_smf_restart_service() != 0) { (void) fprintf(stderr, gettext("Unable to restart smb service. " "Run 'svcs -xv smb/server' for more information.")); } } /* * smbadm_join * * Join a domain or workgroup. * * When joining a domain, we may receive the username, password and * domain name in any of the following combinations. Note that the * password is optional on the command line: if it is not provided, * we will prompt for it later. * * username+password domain * domain\username+password * domain/username+password * username@domain * * We allow domain\name+password or domain/name+password but not * name+password@domain because @ is a valid password character. * * If the username and domain name are passed as separate command * line arguments, we process them directly. Otherwise we separate * them and continue as if they were separate command line arguments. */ static int smbadm_join(int argc, char **argv) { char buf[MAXHOSTNAMELEN * 2]; char *domain = NULL; char *username = NULL; char *container = NULL; uint32_t mode = 0; boolean_t confirm = B_TRUE; int option; while ((option = getopt(argc, argv, "c:pu:wy")) != -1) { if (mode != 0) { (void) fprintf(stderr, gettext( "join options are mutually exclusive\n")); smbadm_usage(B_FALSE); } switch (option) { case 'c': container = optarg; break; case 'p': mode = SMB_SECMODE_DOMAIN; /* leave username = NULL */ break; case 'u': mode = SMB_SECMODE_DOMAIN; username = optarg; break; case 'w': mode = SMB_SECMODE_WORKGRP; break; case 'y': confirm = B_FALSE; break; default: smbadm_usage(B_FALSE); break; } } if (optind < argc) domain = argv[optind]; if (username != NULL && domain == NULL) { /* * The domain was not specified as a separate * argument, check for the combination forms. */ (void) strlcpy(buf, username, sizeof (buf)); smbadm_extract_domain(buf, &username, &domain); } if ((domain == NULL) || (*domain == '\0')) { (void) fprintf(stderr, gettext("missing %s name\n"), (mode == SMB_SECMODE_WORKGRP) ? "workgroup" : "domain"); smbadm_usage(B_FALSE); } if (mode == SMB_SECMODE_WORKGRP) { return (smbadm_join_workgroup(domain, confirm)); } return (smbadm_join_domain(domain, container, username, confirm)); } /* * Workgroups comprise a collection of standalone, independently administered * computers that use a common workgroup name. This is a peer-to-peer model * with no formal membership mechanism. */ static int smbadm_join_workgroup(const char *workgroup, boolean_t confirm) { smb_joininfo_t jdi; smb_joinres_t jdres; uint32_t status; bzero(&jdres, sizeof (jdres)); bzero(&jdi, sizeof (jdi)); jdi.mode = SMB_SECMODE_WORKGRP; (void) strlcpy(jdi.domain_name, workgroup, sizeof (jdi.domain_name)); (void) strtrim(jdi.domain_name, " \t\n"); if (smb_name_validate_workgroup(jdi.domain_name) != ERROR_SUCCESS) { (void) fprintf(stderr, gettext("workgroup name is invalid\n")); smbadm_usage(B_FALSE); } if (confirm && !smbadm_join_confirm(jdi.domain_name)) return (0); smbadm_update_groups(SMBADM_GRP_DELMEMBER); // before "un-join" if ((status = smb_join(&jdi, &jdres)) != NT_STATUS_SUCCESS) { (void) fprintf(stderr, gettext("failed to join %s: %s\n"), jdi.domain_name, xlate_nt_status(status)); return (1); } (void) printf(gettext("Successfully joined %s\n"), jdi.domain_name); smbadm_restart_service(); return (0); } /* * Domains comprise a centrally administered group of computers and accounts * that share a common security and administration policy and database. * Computers must join a domain and become domain members, which requires * an administrator level account name. * * The '+' character is invalid within a username. We allow the password * to be appended to the username using '+' as a scripting convenience. * See details above smbadm_join() */ static int smbadm_join_domain(const char *domain, const char *container, const char *username, boolean_t confirm) { smb_joininfo_t jdi; smb_joinres_t jdres; char *passwd_prompt; char *p; int len, rc; bzero(&jdres, sizeof (jdres)); bzero(&jdi, sizeof (jdi)); jdi.mode = SMB_SECMODE_DOMAIN; (void) strlcpy(jdi.domain_name, domain, sizeof (jdi.domain_name)); (void) strtrim(jdi.domain_name, " \t\n"); if (container != NULL) { if (strlcpy(jdi.container_name, container, sizeof (jdi.container_name)) >= sizeof (jdi.container_name)) { (void) fprintf(stderr, gettext("container name is " "too long\n")); smbadm_usage(B_FALSE); } } if (smb_name_validate_domain(jdi.domain_name) != ERROR_SUCCESS) { (void) fprintf(stderr, gettext("domain name is invalid\n")); smbadm_usage(B_FALSE); } if (confirm && !smbadm_join_confirm(jdi.domain_name)) return (0); /* * Note: username is null for "unsecure join" * (join using a pre-created computer account) * Not implemented. */ if (username == NULL) { (void) fprintf(stderr, gettext("username missing\n")); smbadm_usage(B_FALSE); } /* * Check for "username+password" as described above. */ if ((p = strchr(username, '+')) != NULL) { ++p; len = (int)(p - username); if (len > sizeof (jdi.domain_name)) len = sizeof (jdi.domain_name); (void) strlcpy(jdi.domain_username, username, len); (void) strlcpy(jdi.domain_passwd, p, sizeof (jdi.domain_passwd)); } else { (void) strlcpy(jdi.domain_username, username, sizeof (jdi.domain_username)); } if (smb_name_validate_account(jdi.domain_username) != ERROR_SUCCESS) { (void) fprintf(stderr, gettext("username contains invalid characters\n")); smbadm_usage(B_FALSE); } if (*jdi.domain_passwd == '\0') { if (isatty(fileno(stdin))) { passwd_prompt = gettext("Enter domain password: "); if ((p = getpassphrase(passwd_prompt)) == NULL) { (void) fprintf(stderr, gettext( "missing password\n")); smbadm_usage(B_FALSE); } (void) strlcpy(jdi.domain_passwd, p, sizeof (jdi.domain_passwd)); } else { /* Just read from stdin, no prompt. */ char *pbuf = NULL; size_t pblen = 0; len = getline(&pbuf, &pblen, stdin); /* trim the ending newline if it exists */ if (len > 0 && pbuf[len - 1] == '\n') { pbuf[len - 1] = '\0'; len--; } if (len <= 0) { (void) fprintf(stderr, gettext( "missing password\n")); smbadm_usage(B_FALSE); } (void) strlcpy(jdi.domain_passwd, pbuf, sizeof (jdi.domain_passwd)); } } (void) printf(gettext("Joining %s ... this may take a minute ...\n"), jdi.domain_name); rc = smb_join(&jdi, &jdres); if (rc != 0) { (void) printf(gettext("Cannot call the SMB service. " " (error %d: %s) " "Please check the service status " "(svcs -vx network/smb/server)\n"), rc, strerror(rc)); bzero(&jdi, sizeof (jdi)); return (1); } switch (jdres.status) { case NT_STATUS_SUCCESS: (void) printf(gettext( "Successfully joined domain %s using AD server %s\n"), jdi.domain_name, jdres.dc_name); bzero(&jdi, sizeof (jdi)); smbadm_update_groups(SMBADM_GRP_ADDMEMBER); // after join smbadm_restart_service(); return (0); case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND: /* See: smb_ads_lookup_msdcs */ (void) fprintf(stderr, gettext( "failed to find any AD servers for domain: %s\n"), jdi.domain_name); goto common; case NT_STATUS_BAD_NETWORK_PATH: /* See: smbrdr_ctx_new / smb_ctx_resolve */ (void) fprintf(stderr, gettext( "failed to resolve address of AD server: %s\n"), jdres.dc_name); goto common; case NT_STATUS_NETWORK_ACCESS_DENIED: /* See: smbrdr_ctx_new / smb_ctx_get_ssn */ (void) fprintf(stderr, gettext( "failed to authenticate with AD server: %s\n"), jdres.dc_name); goto common; case NT_STATUS_BAD_NETWORK_NAME: /* * See: smbrdr_ctx_new / smb_ctx_get_tree * and: ndr_rpc_bind / smb_fh_open */ (void) fprintf(stderr, gettext( "failed connecting to services on AD server: %s\n"), jdres.dc_name); goto common; default: (void) fprintf(stderr, gettext( "failed to join domain %s\n"), jdi.domain_name); if (jdres.dc_name[0] != '\0') { (void) fprintf(stderr, gettext( "using AD server: %s\n"), jdres.dc_name); } /* FALLTHROUGH */ common: if (jdres.join_err != 0) { (void) fprintf(stderr, "%s\n", smb_ads_strerror(jdres.join_err)); } else if (jdres.status != 0) { (void) fprintf(stderr, "(%s)\n", xlate_nt_status(jdres.status)); } (void) fprintf(stderr, gettext("Please refer to the " "service log for more information.\n")); bzero(&jdi, sizeof (jdi)); return (1); } } /* * We want to process the user and domain names as separate strings. * Check for names of the forms below and separate the components as * required. * * name@domain * domain\name * domain/name * * If we encounter any of the forms above in arg, the @, / or \ * separator is replaced by \0 and the username and domain pointers * are changed to point to the appropriate components (in arg). * * If none of the separators are encountered, the username and domain * pointers remain unchanged. */ static void smbadm_extract_domain(char *arg, char **username, char **domain) { char *p; if ((p = strpbrk(arg, "/\\@")) != NULL) { if (*p == '@') { *p = '\0'; ++p; if (strchr(arg, '+') != NULL) return; *domain = p; *username = arg; } else { *p = '\0'; ++p; *username = p; *domain = arg; } } } /* * smbadm_update_groups * Add or remove "Domain Admins@mydomain" to/from the * local administrators group. * * Similar to: smbadm_group_add_del_member */ static void smbadm_update_groups(smbadm_grp_action_t act) { char sidstr[SMB_SID_STRSZ]; char gname[] = "administrators"; // must be writable smb_gsid_t msid; int rc; /* * Compose the (well-known) SID for "Domain Admins" * which is {domain-SID}-512 */ rc = smb_config_getstr(SMB_CI_DOMAIN_SID, sidstr, sizeof (sidstr)); if (rc != 0) { (void) fprintf(stderr, gettext("Update local groups: no domain SID\n")); return; } (void) strlcat(sidstr, "-512", sizeof (sidstr)); msid.gs_type = SidTypeGroup; msid.gs_sid = smb_sid_fromstr(sidstr); if (msid.gs_sid == NULL) { (void) fprintf(stderr, gettext("Update local groups: no memory for SID\n")); return; } switch (act) { case SMBADM_GRP_ADDMEMBER: rc = smb_lgrp_add_member(gname, msid.gs_sid, msid.gs_type); // suppress "already in group" if (rc == SMB_LGRP_MEMBER_IN_GROUP) rc = 0; break; case SMBADM_GRP_DELMEMBER: rc = smb_lgrp_del_member(gname, msid.gs_sid, msid.gs_type); // supress "not in group" if (rc == SMB_LGRP_MEMBER_NOT_IN_GROUP) rc = 0; break; default: rc = SMB_LGRP_INTERNAL_ERROR; break; } smb_sid_free(msid.gs_sid); if (rc != SMB_LGRP_SUCCESS) { (void) fprintf(stderr, gettext("Update local groups: can't update DB, %s\n"), smb_lgrp_strerror(rc)); } } /* * smbadm_list * * Displays current security mode and domain/workgroup name. */ /*ARGSUSED*/ static int smbadm_list(int argc, char **argv) { char domain[MAXHOSTNAMELEN]; char fqdn[MAXHOSTNAMELEN]; char srvname[MAXHOSTNAMELEN]; char modename[16]; int rc; smb_inaddr_t srvipaddr; char ipstr[INET6_ADDRSTRLEN]; rc = smb_config_getstr(SMB_CI_SECURITY, modename, sizeof (modename)); if (rc != SMBD_SMF_OK) { (void) fprintf(stderr, gettext("cannot determine the operational mode\n")); return (1); } if (smb_getdomainname(domain, sizeof (domain)) != 0) { (void) fprintf(stderr, gettext("failed to get the %s name\n"), modename); return (1); } if (strcmp(modename, "workgroup") == 0) { (void) printf(gettext("[*] [%s]\n"), domain); return (0); } (void) printf(gettext("[*] [%s]\n"), domain); if ((smb_getfqdomainname(fqdn, sizeof (fqdn)) == 0) && (*fqdn != '\0')) (void) printf(gettext("[*] [%s]\n"), fqdn); if ((smb_get_dcinfo(srvname, MAXHOSTNAMELEN, &srvipaddr) == NT_STATUS_SUCCESS) && (*srvname != '\0') && (!smb_inet_iszero(&srvipaddr))) { (void) smb_inet_ntop(&srvipaddr, ipstr, SMB_IPSTRLEN(srvipaddr.a_family)); (void) printf(gettext("\t[+%s] [%s]\n"), srvname, ipstr); } /* Print the local and domain SID. */ smb_domain_show(); return (0); } /* * smbadm_lookup * * Lookup the SID for a given account (user or group) */ static int smbadm_lookup(int argc, char **argv) { int i; boolean_t parsable = B_FALSE; char option; if (argc < 2) { (void) fprintf(stderr, gettext("missing account name\n")); smbadm_usage(B_FALSE); } while ((option = getopt(argc, argv, "p")) != -1) { switch (option) { case 'p': parsable = B_TRUE; break; default: smbadm_usage(B_FALSE); break; } } for (i = optind; i < argc; i++) { if (strncmp(argv[i], "S-1-", 4) == 0) smbadm_lookup_sid(argv[i], parsable); else smbadm_lookup_name(argv[i], parsable); } return (0); } static void smbadm_lookup_name(char *name, boolean_t parsable) { lsa_account_t acct; int rc; if ((rc = smb_lookup_name(name, SidTypeUnknown, &acct)) != 0) { (void) fprintf(stderr, gettext( "\t\t%s: lookup name failed, rc=%d\n"), name, rc); return; } if (acct.a_status != NT_STATUS_SUCCESS) { (void) fprintf(stderr, gettext("\t\t%s [%s]\n"), name, xlate_nt_status(acct.a_status)); return; } if (parsable) { (void) printf("%s:%u:%s:%s\n", acct.a_sid, acct.a_sidtype, acct.a_name, acct.a_domain); } else { (void) printf("\t%s\n", acct.a_sid); } } static void smbadm_lookup_sid(char *sidstr, boolean_t parsable) { lsa_account_t acct; int rc; if ((rc = smb_lookup_sid(sidstr, &acct)) != 0) { (void) fprintf(stderr, gettext( "\t\t%s: lookup SID failed, rc=%d\n"), sidstr, rc); return; } if (acct.a_status != NT_STATUS_SUCCESS) { (void) fprintf(stderr, gettext("\t\t%s [%s]\n"), sidstr, xlate_nt_status(acct.a_status)); return; } if (parsable) { (void) printf("%s:%u:%s:%s\n", acct.a_sid, acct.a_sidtype, acct.a_name, acct.a_domain); } else { (void) printf("\t%s\\%s\n", acct.a_domain, acct.a_name); } } /* * smbadm_group_create * * Creates a local SMB group */ static int smbadm_group_create(int argc, char **argv) { char *gname = NULL; char *desc = NULL; int option; int status; while ((option = getopt(argc, argv, "d:")) != -1) { switch (option) { case 'd': desc = optarg; break; default: smbadm_usage(B_FALSE); } } gname = argv[optind]; if (optind >= argc || gname == NULL || *gname == '\0') { (void) fprintf(stderr, gettext("missing group name\n")); smbadm_usage(B_FALSE); } status = smb_lgrp_add(gname, desc); if (status != SMB_LGRP_SUCCESS) { (void) fprintf(stderr, gettext("failed to create %s (%s)\n"), gname, smb_lgrp_strerror(status)); } else { (void) printf(gettext("%s created\n"), gname); } return (status); } /* * smbadm_group_dump_members * * Dump group members details. */ static void smbadm_group_dump_members(smb_gsid_t *members, int num, boolean_t show_sids) { char sidstr[SMB_SID_STRSZ]; lsa_account_t acct; int i; if (num == 0) { (void) printf(gettext("\tNo members\n")); return; } (void) printf(gettext("\tMembers:\n")); for (i = 0; i < num; i++) { smb_sid_tostr(members[i].gs_sid, sidstr); if (!show_sids && smb_lookup_sid(sidstr, &acct) == 0) { if (acct.a_status == NT_STATUS_SUCCESS) smbadm_group_show_name(acct.a_domain, acct.a_name); else (void) printf(gettext("\t\t%s [%s]\n"), sidstr, xlate_nt_status(acct.a_status)); } else { (void) printf(gettext("\t\t%s\n"), sidstr); } } } static void smbadm_group_show_name(const char *domain, const char *name) { if (strchr(domain, '.') != NULL) (void) printf("\t\t%s@%s\n", name, domain); else (void) printf("\t\t%s\\%s\n", domain, name); } /* * smbadm_group_dump_privs * * Dump group privilege details. */ static void smbadm_group_dump_privs(smb_privset_t *privs) { smb_privinfo_t *pinfo; char *pstatus; int i; (void) printf(gettext("\tPrivileges: \n")); for (i = 0; i < privs->priv_cnt; i++) { pinfo = smb_priv_getbyvalue(privs->priv[i].luid.lo_part); if ((pinfo == NULL) || (pinfo->flags & PF_PRESENTABLE) == 0) continue; switch (privs->priv[i].attrs) { case SE_PRIVILEGE_ENABLED: pstatus = "On"; break; case SE_PRIVILEGE_DISABLED: pstatus = "Off"; break; default: pstatus = "Unknown"; break; } (void) printf(gettext("\t\t%s: %s\n"), pinfo->name, pstatus); } if (privs->priv_cnt == 0) (void) printf(gettext("\t\tNo privileges\n")); } /* * smbadm_group_dump * * Dump group details. */ static void smbadm_group_dump(smb_group_t *grp, boolean_t show_mem, boolean_t show_privs, boolean_t show_sids) { char sidstr[SMB_SID_STRSZ]; (void) printf(gettext("%s (%s)\n"), grp->sg_name, grp->sg_cmnt); smb_sid_tostr(grp->sg_id.gs_sid, sidstr); (void) printf(gettext("\tSID: %s\n"), sidstr); if (show_privs) smbadm_group_dump_privs(grp->sg_privs); if (show_mem) smbadm_group_dump_members(grp->sg_members, grp->sg_nmembers, show_sids); } /* * smbadm_group_show * */ static int smbadm_group_show(int argc, char **argv) { char *gname = NULL; boolean_t show_members = B_FALSE; boolean_t show_privs = B_FALSE; boolean_t show_sids = B_FALSE; int option; int status; smb_group_t grp; smb_giter_t gi; while ((option = getopt(argc, argv, "mps")) != -1) { switch (option) { case 'm': show_members = B_TRUE; break; case 'p': show_privs = B_TRUE; break; case 's': show_sids = B_TRUE; break; default: smbadm_usage(B_FALSE); } } gname = argv[optind]; if (optind >= argc || gname == NULL || *gname == '\0') gname = "*"; if (strcmp(gname, "*")) { status = smb_lgrp_getbyname(gname, &grp); if (status == SMB_LGRP_SUCCESS) { smbadm_group_dump(&grp, show_members, show_privs, show_sids); smb_lgrp_free(&grp); } else { (void) fprintf(stderr, gettext("failed to find %s (%s)\n"), gname, smb_lgrp_strerror(status)); } return (status); } if ((status = smb_lgrp_iteropen(&gi)) != SMB_LGRP_SUCCESS) { (void) fprintf(stderr, gettext("failed to list groups (%s)\n"), smb_lgrp_strerror(status)); return (status); } while ((status = smb_lgrp_iterate(&gi, &grp)) == SMB_LGRP_SUCCESS) { smbadm_group_dump(&grp, show_members, show_privs, show_sids); smb_lgrp_free(&grp); } smb_lgrp_iterclose(&gi); if ((status != SMB_LGRP_NO_MORE) || smb_lgrp_itererror(&gi)) { if (status != SMB_LGRP_NO_MORE) smb_syslog(LOG_ERR, "smb_lgrp_iterate: %s", smb_lgrp_strerror(status)); (void) fprintf(stderr, gettext("\nAn error occurred while retrieving group data.\n" "Check the system log for more information.\n")); return (status); } return (0); } /* * smbadm_group_delete */ static int smbadm_group_delete(int argc, char **argv) { char *gname = NULL; int status; gname = argv[optind]; if (optind >= argc || gname == NULL || *gname == '\0') { (void) fprintf(stderr, gettext("missing group name\n")); smbadm_usage(B_FALSE); } status = smb_lgrp_delete(gname); if (status != SMB_LGRP_SUCCESS) { (void) fprintf(stderr, gettext("failed to delete %s (%s)\n"), gname, smb_lgrp_strerror(status)); } else { (void) printf(gettext("%s deleted\n"), gname); } return (status); } /* * smbadm_group_rename */ static int smbadm_group_rename(int argc, char **argv) { char *gname = NULL; char *ngname = NULL; int status; gname = argv[optind]; if (optind++ >= argc || gname == NULL || *gname == '\0') { (void) fprintf(stderr, gettext("missing group name\n")); smbadm_usage(B_FALSE); } ngname = argv[optind]; if (optind >= argc || ngname == NULL || *ngname == '\0') { (void) fprintf(stderr, gettext("missing new group name\n")); smbadm_usage(B_FALSE); } status = smb_lgrp_rename(gname, ngname); if (status != SMB_LGRP_SUCCESS) { if (status == SMB_LGRP_EXISTS) (void) fprintf(stderr, gettext("failed to rename '%s' (%s already " "exists)\n"), gname, ngname); else (void) fprintf(stderr, gettext("failed to rename '%s' (%s)\n"), gname, smb_lgrp_strerror(status)); } else { (void) printf(gettext("'%s' renamed to '%s'\n"), gname, ngname); } return (status); } /* * smbadm_group_setprop * * Set the group properties. */ static int smbadm_group_setprop(int argc, char **argv) { char *gname = NULL; smbadm_prop_t props[SMBADM_NPROP]; smbadm_prop_handle_t *phandle; int option; int pcnt = 0; int ret = 0; int p; bzero(props, SMBADM_NPROP * sizeof (smbadm_prop_t)); while ((option = getopt(argc, argv, "p:")) != -1) { switch (option) { case 'p': if (pcnt >= SMBADM_NPROP) { (void) fprintf(stderr, gettext("exceeded number of supported" " properties\n")); smbadm_usage(B_FALSE); } if (smbadm_prop_parse(optarg, &props[pcnt++]) != 0) smbadm_usage(B_FALSE); break; default: smbadm_usage(B_FALSE); } } if (pcnt == 0) { (void) fprintf(stderr, gettext("missing property=value argument\n")); smbadm_usage(B_FALSE); } gname = argv[optind]; if (optind >= argc || gname == NULL || *gname == '\0') { (void) fprintf(stderr, gettext("missing group name\n")); smbadm_usage(B_FALSE); } for (p = 0; p < pcnt; p++) { phandle = smbadm_prop_gethandle(props[p].p_name); if (phandle) { if (phandle->p_setfn(gname, &props[p]) != 0) ret = 1; } } return (ret); } /* * smbadm_group_getprop * * Get the group properties. */ static int smbadm_group_getprop(int argc, char **argv) { char *gname = NULL; smbadm_prop_t props[SMBADM_NPROP]; smbadm_prop_handle_t *phandle; int option; int pcnt = 0; int ret = 0; int p; bzero(props, SMBADM_NPROP * sizeof (smbadm_prop_t)); while ((option = getopt(argc, argv, "p:")) != -1) { switch (option) { case 'p': if (pcnt >= SMBADM_NPROP) { (void) fprintf(stderr, gettext("exceeded number of supported" " properties\n")); smbadm_usage(B_FALSE); } if (smbadm_prop_parse(optarg, &props[pcnt++]) != 0) smbadm_usage(B_FALSE); break; default: smbadm_usage(B_FALSE); } } gname = argv[optind]; if (optind >= argc || gname == NULL || *gname == '\0') { (void) fprintf(stderr, gettext("missing group name\n")); smbadm_usage(B_FALSE); } if (pcnt == 0) { /* * If no property has be specified then get * all the properties. */ pcnt = SMBADM_NPROP; for (p = 0; p < pcnt; p++) props[p].p_name = smbadm_ptable[p].p_name; } for (p = 0; p < pcnt; p++) { phandle = smbadm_prop_gethandle(props[p].p_name); if (phandle) { if (phandle->p_getfn(gname, &props[p]) != 0) ret = 1; } } return (ret); } /* * smbadm_group_addmember * */ static int smbadm_group_addmember(int argc, char **argv) { char *gname = NULL; char **mname; int option; int mcnt = 0; int ret = 0; int i; mname = (char **)malloc(argc * sizeof (char *)); if (mname == NULL) { warn(gettext("failed to add group member")); return (1); } bzero(mname, argc * sizeof (char *)); while ((option = getopt(argc, argv, "m:")) != -1) { switch (option) { case 'm': mname[mcnt++] = optarg; break; default: free(mname); smbadm_usage(B_FALSE); } } if (mcnt == 0) { (void) fprintf(stderr, gettext("missing member name\n")); free(mname); smbadm_usage(B_FALSE); } gname = argv[optind]; if (optind >= argc || gname == NULL || *gname == 0) { (void) fprintf(stderr, gettext("missing group name\n")); free(mname); smbadm_usage(B_FALSE); } for (i = 0; i < mcnt; i++) { if (mname[i] == NULL) continue; ret |= smbadm_group_add_del_member( gname, mname[i], SMBADM_GRP_ADDMEMBER); } free(mname); return (ret); } /* * smbadm_group_delmember */ static int smbadm_group_delmember(int argc, char **argv) { char *gname = NULL; char **mname; int option; int mcnt = 0; int ret = 0; int i; mname = (char **)malloc(argc * sizeof (char *)); if (mname == NULL) { warn(gettext("failed to delete group member")); return (1); } bzero(mname, argc * sizeof (char *)); while ((option = getopt(argc, argv, "m:")) != -1) { switch (option) { case 'm': mname[mcnt++] = optarg; break; default: free(mname); smbadm_usage(B_FALSE); } } if (mcnt == 0) { (void) fprintf(stderr, gettext("missing member name\n")); free(mname); smbadm_usage(B_FALSE); } gname = argv[optind]; if (optind >= argc || gname == NULL || *gname == 0) { (void) fprintf(stderr, gettext("missing group name\n")); free(mname); smbadm_usage(B_FALSE); } for (i = 0; i < mcnt; i++) { ret = 0; if (mname[i] == NULL) continue; ret |= smbadm_group_add_del_member( gname, mname[i], SMBADM_GRP_DELMEMBER); } free(mname); return (ret); } static int smbadm_group_add_del_member(char *gname, char *mname, smbadm_grp_action_t act) { lsa_account_t acct; smb_gsid_t msid; char *sidstr; char *act_str = NULL; int rc; if (strncmp(mname, "S-1-", 4) == 0) { /* * We are given a SID. Just use it. * * We'd like the real account type if we can get it, * but don't want to error out if we can't get it. * Lacking other info, assume it's a group. */ sidstr = mname; rc = smb_lookup_sid(sidstr, &acct); if ((rc != 0) || (acct.a_status != NT_STATUS_SUCCESS)) acct.a_sidtype = SidTypeGroup; } else { rc = smb_lookup_name(mname, SidTypeUnknown, &acct); if ((rc != 0) || (acct.a_status != NT_STATUS_SUCCESS)) { (void) fprintf(stderr, gettext("%s: name lookup failed\n"), mname); return (1); } sidstr = acct.a_sid; } msid.gs_type = acct.a_sidtype; if ((msid.gs_sid = smb_sid_fromstr(sidstr)) == NULL) { (void) fprintf(stderr, gettext("%s: no memory for SID\n"), sidstr); return (1); } switch (act) { case SMBADM_GRP_ADDMEMBER: act_str = gettext("add"); rc = smb_lgrp_add_member(gname, msid.gs_sid, msid.gs_type); break; case SMBADM_GRP_DELMEMBER: act_str = gettext("remove"); rc = smb_lgrp_del_member(gname, msid.gs_sid, msid.gs_type); break; default: rc = SMB_LGRP_INTERNAL_ERROR; break; } smb_sid_free(msid.gs_sid); if (rc != SMB_LGRP_SUCCESS) { (void) fprintf(stderr, gettext("failed to %s %s (%s)\n"), act_str, mname, smb_lgrp_strerror(rc)); return (1); } return (0); } static int smbadm_user_delete(int argc, char **argv) { int error; char *user = NULL; user = argv[optind]; if (optind >= argc || user == NULL || *user == '\0') { (void) fprintf(stderr, gettext("missing user name\n")); smbadm_usage(B_FALSE); } error = smb_pwd_setcntl(user, SMB_PWC_DELETE); if (error == SMB_PWE_SUCCESS) (void) printf(gettext("%s has been deleted.\n"), user); else (void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error)); return (error); } static int smbadm_user_disable(int argc, char **argv) { int error; char *user = NULL; user = argv[optind]; if (optind >= argc || user == NULL || *user == '\0') { (void) fprintf(stderr, gettext("missing user name\n")); smbadm_usage(B_FALSE); } error = smb_pwd_setcntl(user, SMB_PWC_DISABLE); if (error == SMB_PWE_SUCCESS) (void) printf(gettext("%s is disabled.\n"), user); else (void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error)); return (error); } static int smbadm_user_enable(int argc, char **argv) { int error; char *user = NULL; user = argv[optind]; if (optind >= argc || user == NULL || *user == '\0') { (void) fprintf(stderr, gettext("missing user name\n")); smbadm_usage(B_FALSE); } error = smb_pwd_setcntl(user, SMB_PWC_ENABLE); if (error == SMB_PWE_SUCCESS) (void) printf(gettext("%s is enabled.\n"), user); else (void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error)); return (error); } int main(int argc, char **argv) { int ret; int i; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); (void) malloc(0); /* satisfy libumem dependency */ progname = basename(argv[0]); if (is_system_labeled()) { (void) fprintf(stderr, gettext("Trusted Extensions not supported\n")); return (1); } if (argc < 2) { (void) fprintf(stderr, gettext("missing command\n")); smbadm_usage(B_FALSE); } /* * Special case "cmd --help/-?" */ if (strcmp(argv[1], "-?") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) smbadm_usage(B_TRUE); for (i = 0; i < SMBADM_NCMD; ++i) { curcmd = &smbadm_cmdtable[i]; if (strcasecmp(argv[1], curcmd->name) == 0) { if (argc > 2) { /* cmd subcmd --help/-? */ if (strcmp(argv[2], "-?") == 0 || strcmp(argv[2], "--help") == 0 || strcmp(argv[2], "-h") == 0) smbadm_usage(B_TRUE); } if (!smbadm_checkauth(curcmd->auth)) { (void) fprintf(stderr, gettext("%s: %s: authorization denied\n"), progname, curcmd->name); return (1); } if ((ret = smbadm_init()) != 0) return (ret); ret = curcmd->func(argc - 1, &argv[1]); smbadm_fini(); return (ret); } } curcmd = NULL; (void) fprintf(stderr, gettext("unknown subcommand (%s)\n"), argv[1]); smbadm_usage(B_FALSE); return (2); } static int smbadm_init(void) { int rc; if ((curcmd->flags & SMBADM_CMDF_KMOD) != 0) { if ((rc = smb_kmod_bind(B_FALSE)) != 0) { (void) fprintf(stderr, gettext("failed to open driver (%s)\n"), strerror(rc)); return (1); } } if ((curcmd->flags & SMBADM_CMDF_GROUP) != 0) { if ((rc = smb_lgrp_start()) != SMB_LGRP_SUCCESS) { (void) fprintf(stderr, gettext("failed to initialize (%s)\n"), smb_lgrp_strerror(rc)); return (1); } } if ((curcmd->flags & SMBADM_CMDF_USER) != 0) { smb_pwd_init(B_FALSE); } return (0); } static void smbadm_fini(void) { if ((curcmd->flags & SMBADM_CMDF_KMOD) != 0) { smb_kmod_unbind(); } if ((curcmd->flags & SMBADM_CMDF_GROUP) != 0) { smb_lgrp_stop(); } if ((curcmd->flags & SMBADM_CMDF_USER) != 0) { smb_pwd_fini(); } } static boolean_t smbadm_checkauth(const char *auth) { struct passwd *pw; if ((pw = getpwuid(getuid())) == NULL) return (B_FALSE); if (chkauthattr(auth, pw->pw_name) == 0) return (B_FALSE); return (B_TRUE); } static boolean_t smbadm_prop_validate(smbadm_prop_t *prop, boolean_t chkval) { smbadm_prop_handle_t *pinfo; int i; for (i = 0; i < SMBADM_NPROP; i++) { pinfo = &smbadm_ptable[i]; if (strcmp(pinfo->p_name, prop->p_name) == 0) { if (pinfo->p_chkfn && chkval) return (pinfo->p_chkfn(prop)); return (B_TRUE); } } (void) fprintf(stderr, gettext("unrecognized property '%s'\n"), prop->p_name); return (B_FALSE); } static int smbadm_prop_parse(char *arg, smbadm_prop_t *prop) { boolean_t parse_value; char *equal; if (arg == NULL) return (2); prop->p_name = prop->p_value = NULL; if (strcmp(curcmd->name, "set") == 0) parse_value = B_TRUE; else parse_value = B_FALSE; prop->p_name = arg; if (parse_value) { equal = strchr(arg, '='); if (equal == NULL) return (2); *equal++ = '\0'; prop->p_value = equal; } if (smbadm_prop_validate(prop, parse_value) == B_FALSE) return (2); return (0); } static smbadm_prop_handle_t * smbadm_prop_gethandle(char *pname) { int i; for (i = 0; i < SMBADM_NPROP; i++) if (strcmp(pname, smbadm_ptable[i].p_name) == 0) return (&smbadm_ptable[i]); return (NULL); } static int smbadm_setprop_desc(char *gname, smbadm_prop_t *prop) { int status; status = smb_lgrp_setcmnt(gname, prop->p_value); if (status != SMB_LGRP_SUCCESS) { (void) fprintf(stderr, gettext("failed to modify the group description (%s)\n"), smb_lgrp_strerror(status)); return (1); } (void) printf(gettext("%s: description modified\n"), gname); return (0); } static int smbadm_getprop_desc(char *gname, smbadm_prop_t *prop) { char *cmnt = NULL; int status; status = smb_lgrp_getcmnt(gname, &cmnt); if (status != SMB_LGRP_SUCCESS) { (void) fprintf(stderr, gettext("failed to get the group description (%s)\n"), smb_lgrp_strerror(status)); return (1); } (void) printf(gettext("\t%s: %s\n"), prop->p_name, cmnt); free(cmnt); return (0); } static int smbadm_group_setpriv(char *gname, uint8_t priv_id, smbadm_prop_t *prop) { boolean_t enable; int status; int ret; if (strcasecmp(prop->p_value, "on") == 0) { (void) printf(gettext("Enabling %s privilege "), prop->p_name); enable = B_TRUE; } else { (void) printf(gettext("Disabling %s privilege "), prop->p_name); enable = B_FALSE; } status = smb_lgrp_setpriv(gname, priv_id, enable); if (status == SMB_LGRP_SUCCESS) { (void) printf(gettext("succeeded\n")); ret = 0; } else { (void) printf(gettext("failed: %s\n"), smb_lgrp_strerror(status)); ret = 1; } return (ret); } static int smbadm_group_getpriv(char *gname, uint8_t priv_id, smbadm_prop_t *prop) { boolean_t enable; int status; status = smb_lgrp_getpriv(gname, priv_id, &enable); if (status != SMB_LGRP_SUCCESS) { (void) fprintf(stderr, gettext("failed to get %s (%s)\n"), prop->p_name, smb_lgrp_strerror(status)); return (1); } (void) printf(gettext("\t%s: %s\n"), prop->p_name, (enable) ? "On" : "Off"); return (0); } static int smbadm_setprop_tkowner(char *gname, smbadm_prop_t *prop) { return (smbadm_group_setpriv(gname, SE_TAKE_OWNERSHIP_LUID, prop)); } static int smbadm_getprop_tkowner(char *gname, smbadm_prop_t *prop) { return (smbadm_group_getpriv(gname, SE_TAKE_OWNERSHIP_LUID, prop)); } static int smbadm_setprop_readfile(char *gname, smbadm_prop_t *prop) { return (smbadm_group_setpriv(gname, SE_READ_FILE_LUID, prop)); } static int smbadm_getprop_readfile(char *gname, smbadm_prop_t *prop) { return (smbadm_group_getpriv(gname, SE_READ_FILE_LUID, prop)); } static int smbadm_setprop_writefile(char *gname, smbadm_prop_t *prop) { return (smbadm_group_setpriv(gname, SE_WRITE_FILE_LUID, prop)); } static int smbadm_getprop_writefile(char *gname, smbadm_prop_t *prop) { return (smbadm_group_getpriv(gname, SE_WRITE_FILE_LUID, prop)); } static int smbadm_setprop_backup(char *gname, smbadm_prop_t *prop) { return (smbadm_group_setpriv(gname, SE_BACKUP_LUID, prop)); } static int smbadm_getprop_backup(char *gname, smbadm_prop_t *prop) { return (smbadm_group_getpriv(gname, SE_BACKUP_LUID, prop)); } static int smbadm_setprop_restore(char *gname, smbadm_prop_t *prop) { return (smbadm_group_setpriv(gname, SE_RESTORE_LUID, prop)); } static int smbadm_getprop_restore(char *gname, smbadm_prop_t *prop) { return (smbadm_group_getpriv(gname, SE_RESTORE_LUID, prop)); } static boolean_t smbadm_chkprop_priv(smbadm_prop_t *prop) { if (prop->p_value == NULL || *prop->p_value == '\0') { (void) fprintf(stderr, gettext("missing value for '%s'\n"), prop->p_name); return (B_FALSE); } if (strcasecmp(prop->p_value, "on") == 0) return (B_TRUE); if (strcasecmp(prop->p_value, "off") == 0) return (B_TRUE); (void) fprintf(stderr, gettext("%s: unrecognized value for '%s' property\n"), prop->p_value, prop->p_name); return (B_FALSE); } static const char * smbadm_pwd_strerror(int error) { switch (error) { case SMB_PWE_SUCCESS: return (gettext("Success.")); case SMB_PWE_USER_UNKNOWN: return (gettext("User does not exist.")); case SMB_PWE_USER_DISABLE: return (gettext("User is disabled.")); case SMB_PWE_CLOSE_FAILED: case SMB_PWE_OPEN_FAILED: case SMB_PWE_WRITE_FAILED: case SMB_PWE_UPDATE_FAILED: return (gettext("Unexpected failure. " "SMB password database unchanged.")); case SMB_PWE_STAT_FAILED: return (gettext("stat of SMB password file failed.")); case SMB_PWE_BUSY: return (gettext("SMB password database busy. " "Try again later.")); case SMB_PWE_DENIED: return (gettext("Operation not permitted.")); case SMB_PWE_SYSTEM_ERROR: return (gettext("System error.")); default: break; } return (gettext("Unknown error code.")); } /* * Enable libumem debugging by default on DEBUG builds. */ #ifdef DEBUG const char * _umem_debug_init(void) { return ("default,verbose"); /* $UMEM_DEBUG setting */ } const char * _umem_logging_init(void) { return ("fail,contents"); /* $UMEM_LOGGING setting */ } #endif /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2023 RackTop Systems, Inc. */ #ifndef _SMBADM_H #define _SMBADM_H /* * Declarations private to the smbadm command */ #ifdef __cplusplus extern "C" { #endif /* Commands in smbinfo.c */ extern int cmd_list_sess(int, char **); extern int cmd_list_trees(int, char **); extern int cmd_list_ofiles(int, char **); extern int cmd_close_sess(int, char **); extern int cmd_close_ofile(int, char **); #ifdef __cplusplus } #endif #endif /* _SMBADM_H */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2022 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbadm.h" /* * Share types for shiX_type fields - duplicated from smb.h * Don't want to pull that in, and these are "carved in stone" * (from the SMB protocol definitions) */ #ifndef _SHARE_TYPES_DEFINED_ #define _SHARE_TYPES_DEFINED_ #define STYPE_DISKTREE 0x00000000 #define STYPE_PRINTQ 0x00000001 #define STYPE_DEVICE 0x00000002 #define STYPE_IPC 0x00000003 #define STYPE_MASK 0x0000000F #endif /* _SHARE_TYPES_DEFINED_ */ #define MINS (60U) #define HRS (60 * MINS) #define DAYS (24 * HRS) #define TIME_FMT "%F %T %Z" #define _(x) gettext(x) struct flag_tbl { uint32_t flag; const char *name; }; typedef enum user_field { UF_SESS_ID, UF_DOMAIN, UF_ACCOUNT, UF_USER, UF_UID, UF_WORKSTATION, UF_IP, UF_OS, UF_LOGON_TIME, UF_AGE, UF_NUMOPEN, UF_FLAGS, } user_field_t; typedef enum tree_field { TF_ID, TF_TYPE, TF_NUMOPEN, TF_NUMUSERS, TF_TIME, TF_AGE, TF_USERNAME, TF_SHARE, } tree_field_t; typedef enum netfileinfo_field { NFIF_FID, NFIF_UNIQID, NFIF_PERMS, NFIF_NUMLOCKS, NFIF_PATH, NFIF_USERNAME, } netfileinfo_field_t; static ofmt_handle_t cmd_create_handle(int, char **, const char *, ofmt_field_t *); static boolean_t fmt_user(ofmt_arg_t *, char *, uint_t); static boolean_t fmt_tree(ofmt_arg_t *, char *, uint_t); static boolean_t fmt_netfileinfo(ofmt_arg_t *, char *, uint_t); static void print_str(const char *restrict, char *restrict, uint_t); static void print_u32(uint32_t, char *, uint_t); static void print_age(time_t, char *, uint_t); static void print_time(time_t, const char *, char *, uint_t); static void print_flags(struct flag_tbl *, size_t, uint32_t, char *, uint_t); static void print_perms(struct flag_tbl *, size_t, uint32_t, char *, uint_t); static ofmt_field_t smb_user_fields[] = { { "ID", 4, UF_SESS_ID, fmt_user }, { "DOMAIN", 32, UF_DOMAIN, fmt_user }, { "ACCT", 16, UF_ACCOUNT, fmt_user }, { "USER", 32, UF_USER, fmt_user }, { "UID", 12, UF_UID, fmt_user }, { "COMPUTER", 16, UF_WORKSTATION, fmt_user }, { "IP", 15, UF_IP, fmt_user }, { "OS", 8, UF_OS, fmt_user }, { "LOGON", 24, UF_LOGON_TIME, fmt_user }, { "AGE", 16, UF_AGE, fmt_user }, { "NOPEN", 5, UF_NUMOPEN, fmt_user }, { "FLAGS", 12, UF_FLAGS, fmt_user }, { NULL, 0, 0, NULL } }; static const char default_user_fields[] = "IP,USER,NOPEN,AGE,FLAGS"; struct flag_tbl user_flag_tbl[] = { { SMB_ATF_GUEST, "GUEST" }, { SMB_ATF_ANON, "ANON" }, { SMB_ATF_ADMIN, "ADMIN" }, { SMB_ATF_POWERUSER, "POWERUSER" }, { SMB_ATF_BACKUPOP, "BACKUPOP" }, }; static ofmt_field_t smb_tree_fields[] = { { "ID", 4, TF_ID, fmt_tree }, { "TYPE", 6, TF_TYPE, fmt_tree }, { "NOPEN", 6, TF_NUMOPEN, fmt_tree }, { "NUSER", 6, TF_NUMUSERS, fmt_tree }, { "TIME", 24, TF_TIME, fmt_tree }, { "AGE", 12, TF_AGE, fmt_tree }, { "USER", 32, TF_USERNAME, fmt_tree }, { "SHARE", 16, TF_SHARE, fmt_tree }, { NULL, 0, 0, NULL } }; static const char default_tree_fields[] = "TYPE,SHARE,USER,NOPEN,AGE"; static ofmt_field_t smb_netfileinfo_fields[] = { { "ID", 4, NFIF_FID, fmt_netfileinfo }, { "UNIQID", 8, NFIF_UNIQID, fmt_netfileinfo }, { "PERM", 15, NFIF_PERMS, fmt_netfileinfo }, { "NLOCK", 6, NFIF_NUMLOCKS, fmt_netfileinfo }, { "PATH", 32, NFIF_PATH, fmt_netfileinfo }, { "USER", 16, NFIF_USERNAME, fmt_netfileinfo }, { NULL, 0, 0, NULL } }; static const char default_netfileinfo_fields[] = "UNIQID,PATH,USER,NLOCK,PERM"; /* * Flags are the same as "ls -V" and chmod ACLs: * eg: everyone@:rwxpdDaARWcCos:fd----I:allow * See libsec:acltext.c */ static struct flag_tbl nfi_perm_tbl[] = { { ACE_READ_DATA, "r" }, { ACE_WRITE_DATA, "w" }, { ACE_EXECUTE, "x" }, { ACE_APPEND_DATA, "p" }, { ACE_DELETE, "d" }, { ACE_DELETE_CHILD, "D" }, { ACE_READ_ATTRIBUTES, "a" }, { ACE_WRITE_ATTRIBUTES, "A" }, { ACE_READ_NAMED_ATTRS, "R" }, { ACE_WRITE_NAMED_ATTRS, "W" }, { ACE_READ_ACL, "c" }, { ACE_WRITE_ACL, "C" }, { ACE_WRITE_OWNER, "o" }, { ACE_SYNCHRONIZE, "s" }, }; static int do_enum(smb_svcenum_t *, ofmt_handle_t); static void ofmt_fatal(ofmt_handle_t, ofmt_field_t *, ofmt_status_t) __NORETURN; static void fatal(const char *, ...) __NORETURN; time_t now; boolean_t opt_p; boolean_t opt_x; int cmd_list_sess(int argc, char **argv) { ofmt_handle_t hdl; smb_svcenum_t req = { .se_type = SMB_SVCENUM_TYPE_USER, .se_level = 1, .se_nlimit = UINT32_MAX, }; int rc; hdl = cmd_create_handle(argc, argv, default_user_fields, smb_user_fields); rc = do_enum(&req, hdl); ofmt_close(hdl); return (rc); } int cmd_list_trees(int argc, char **argv) { ofmt_handle_t hdl; smb_svcenum_t req = { .se_type = SMB_SVCENUM_TYPE_TREE, .se_level = 1, .se_nlimit = UINT32_MAX, }; int rc; hdl = cmd_create_handle(argc, argv, default_tree_fields, smb_tree_fields); rc = do_enum(&req, hdl); ofmt_close(hdl); return (rc); } int cmd_list_ofiles(int argc, char **argv) { ofmt_handle_t hdl; smb_svcenum_t req = { .se_type = SMB_SVCENUM_TYPE_FILE, .se_level = 1, .se_nlimit = UINT32_MAX, }; int rc; hdl = cmd_create_handle(argc, argv, default_netfileinfo_fields, smb_netfileinfo_fields); rc = do_enum(&req, hdl); ofmt_close(hdl); return (rc); } static ofmt_handle_t cmd_create_handle(int argc, char **argv, const char *def, ofmt_field_t *templ) { const char *fields = def; ofmt_handle_t hdl; ofmt_status_t status; uint_t flags = 0; int c; while ((c = getopt(argc, argv, "Ho:px")) != -1) { switch (c) { case 'H': flags |= OFMT_NOHEADER; break; case 'o': fields = optarg; break; case 'p': opt_p = B_TRUE; flags |= OFMT_PARSABLE; break; case 'x': opt_x = B_TRUE; break; case '?': /* Note: getopt prints an error for us. */ return (NULL); } } status = ofmt_open(fields, templ, flags, 0, &hdl); if (status != OFMT_SUCCESS) ofmt_fatal(hdl, templ, status); return (hdl); } int cmd_close_ofile(int argc, char **argv) { uint_t errs = 0; if (argc < 2) { warnx(_("Missing file id")); return (2); } for (int i = 1; i < argc; i++) { unsigned long ul; int rc; errno = 0; ul = strtoul(argv[i], NULL, 0); if (errno != 0) { warnx(_("Invalid file id '%s'"), argv[i]); return (2); } #ifdef _LP64 if (ul > UINT32_MAX) { warnx(_("File id %lu too large"), ul); return (2); } #endif /* * See SMB_IOC_FILE_CLOSE (ioc.uniqid) * and smb_server_file_close() */ rc = smb_kmod_file_close((uint32_t)ul); if (rc != 0) { /* * Since the user can specify the fid as a decimal * or hex value, we use the string they gave us so * the value displayed matches what we were given. */ warnc(rc, _("Closing fid %s failed"), argv[i]); errs++; } } if (errs > 0) return (1); return (0); } int cmd_close_sess(int argc, char **argv) { const char *client; const char *user = NULL; int rc; if (argc < 2) { warnx(_("clientname and username missing")); return (2); } client = argv[1]; if (argc > 2) { user = argv[2]; } /* * See SMB_IOC_SESSION_CLOSE (ioc.client, ioc.username) * and smb_server_session_close(). The "client" part * can be EITHER the "workstation" or the IP address, * as shown in the "COMPUTER" and "IP" fields in the * output of "list-sessions". The (optional) "user" * part is as shown in "USER" part of that output. */ rc = smb_kmod_session_close(client, user); if (rc != 0) { rc = 1; } return (rc); } static boolean_t fmt_user(ofmt_arg_t *arg, char *buf, uint_t buflen) { smb_netuserinfo_t *ui = arg->ofmt_cbarg; user_field_t field = (user_field_t)arg->ofmt_id; switch (field) { case UF_SESS_ID: (void) snprintf(buf, buflen, "%" PRIu64, ui->ui_session_id); break; case UF_DOMAIN: print_str(ui->ui_domain, buf, buflen); break; case UF_ACCOUNT: print_str(ui->ui_account, buf, buflen); break; case UF_USER: (void) snprintf(buf, buflen, "%s\\%s", ui->ui_domain, ui->ui_account); break; case UF_UID: VERIFY3U(arg->ofmt_width, <, INT_MAX); (void) snprintf(buf, buflen, "%u", ui->ui_posix_uid); break; case UF_WORKSTATION: print_str(ui->ui_workstation, buf, buflen); break; case UF_IP: (void) smb_inet_ntop(&ui->ui_ipaddr, buf, buflen); break; case UF_OS: /* XXX: Lookup string value */ (void) snprintf(buf, buflen, "%" PRId32, ui->ui_native_os); break; case UF_LOGON_TIME: print_time(ui->ui_logon_time, TIME_FMT, buf, buflen); break; case UF_AGE: print_age(now - ui->ui_logon_time, buf, buflen); break; case UF_NUMOPEN: print_u32(ui->ui_numopens, buf, buflen); break; case UF_FLAGS: print_flags(user_flag_tbl, ARRAY_SIZE(user_flag_tbl), ui->ui_flags, buf, buflen); break; default: fatal("%s: invalid field %d", __func__, field); } return (B_TRUE); } static boolean_t fmt_tree_type(uint32_t type, char *buf, uint_t buflen) { switch (type & STYPE_MASK) { case STYPE_DISKTREE: (void) strlcpy(buf, "DISK", buflen); break; case STYPE_PRINTQ: (void) strlcpy(buf, "PRINTQ", buflen); break; case STYPE_DEVICE: (void) strlcpy(buf, "DEVICE", buflen); break; case STYPE_IPC: (void) strlcpy(buf, "IPC", buflen); break; default: (void) snprintf(buf, buflen, "%" PRIx32, type & STYPE_MASK); break; } return (B_TRUE); } static boolean_t fmt_tree(ofmt_arg_t *arg, char *buf, uint_t buflen) { smb_netconnectinfo_t *nc = arg->ofmt_cbarg; tree_field_t field = (tree_field_t)arg->ofmt_id; switch (field) { case TF_ID: (void) snprintf(buf, buflen, "%" PRIu32, nc->ci_id); break; case TF_TYPE: return (fmt_tree_type(nc->ci_type, buf, buflen)); case TF_NUMOPEN: print_u32(nc->ci_numopens, buf, buflen); break; case TF_NUMUSERS: print_u32(nc->ci_numusers, buf, buflen); break; case TF_TIME: print_time(now - nc->ci_time, TIME_FMT, buf, buflen); break; case TF_AGE: print_age(nc->ci_time, buf, buflen); break; case TF_USERNAME: print_str(nc->ci_username, buf, buflen); break; case TF_SHARE: print_str(nc->ci_share, buf, buflen); break; default: fatal("%s: invalid field %d", __func__, field); } return (B_TRUE); } static boolean_t fmt_netfileinfo(ofmt_arg_t *arg, char *buf, uint_t buflen) { smb_netfileinfo_t *fi = arg->ofmt_cbarg; netfileinfo_field_t field = (netfileinfo_field_t)arg->ofmt_id; switch (field) { case NFIF_FID: (void) snprintf(buf, buflen, "%" PRIu16, fi->fi_fid); break; case NFIF_UNIQID: (void) snprintf(buf, buflen, "%" PRIu32, fi->fi_uniqid); break; case NFIF_PERMS: print_perms(nfi_perm_tbl, ARRAY_SIZE(nfi_perm_tbl), fi->fi_permissions, buf, buflen); break; case NFIF_NUMLOCKS: print_u32(fi->fi_numlocks, buf, buflen); break; case NFIF_PATH: print_str(fi->fi_path, buf, buflen); break; case NFIF_USERNAME: print_str(fi->fi_username, buf, buflen); break; default: fatal("%s: invalid field %d", __func__, field); } return (B_TRUE); } static int do_enum(smb_svcenum_t *req, ofmt_handle_t hdl) { smb_netsvc_t *ns; smb_netsvcitem_t *item; uint32_t n = 0; int rc; if (hdl == NULL) return (2); /* exit (2) -- usage */ now = time(NULL); for (;;) { req->se_nskip = n; ns = smb_kmod_enum_init(req); if (ns == NULL) { warnx(_("SMB enum initialization failure")); return (1); } rc = smb_kmod_enum(ns); if (rc != 0) { /* * When the SMB service is not running, expect ENXIO. */ if (rc == ENXIO) { warnx(_("Kernel SMB server not running")); return (1); } warnc(rc, _("SMB enumeration call failed")); return (1); } if (list_is_empty(&ns->ns_list)) break; for (item = list_head(&ns->ns_list); item != NULL; item = list_next(&ns->ns_list, item)) { ofmt_print(hdl, &item->nsi_un); n++; } smb_kmod_enum_fini(ns); } return (0); } static void print_str(const char *restrict src, char *restrict buf, uint_t buflen) { if (src == NULL) { buf[0] = '\0'; return; } (void) strlcpy(buf, src, buflen); } static void print_u32(uint32_t val, char *buf, uint_t buflen) { const char *fmt = opt_p ? "%" PRIu32 : "%'" PRIu32; (void) snprintf(buf, buflen, fmt, val); } static void print_age(time_t amt, char *buf, uint_t buflen) { uint32_t days = 0, hours = 0, mins = 0; if (opt_p) { (void) snprintf(buf, buflen, "%" PRId64, (int64_t)amt); return; } if (amt >= DAYS) { days = amt / DAYS; amt %= DAYS; } if (amt >= HRS) { hours = amt / HRS; amt %= HRS; } if (amt >= MINS) { mins = amt / MINS; amt %= MINS; } if (days > 0) { int n = snprintf(buf, buflen, "%" PRIu32 " days%s", days, (hours > 0 || mins > 0 || amt > 0) ? ", " : ""); VERIFY3U(buflen, >, n); buf += n; buflen -= n; } (void) snprintf(buf, buflen, "%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, hours, mins, amt); } static void print_time(time_t when, const char *fmt, char *buf, uint_t buflen) { const struct tm *tm; if (opt_p) { (void) snprintf(buf, buflen, "%" PRId64, (int64_t)when); return; } tm = localtime(&when); (void) strftime(buf, buflen - 1, fmt, tm); } static void print_flags(struct flag_tbl *tbl, size_t nent, uint32_t val, char *buf, uint_t buflen) { uint_t n = 0; uint_t i; if (opt_x) { (void) snprintf(buf, buflen, "%" PRIx32, val); return; } for (i = 0; i < nent; i++) { if ((val & tbl[i].flag) == 0) continue; if (n > 0) (void) strlcat(buf, ",", buflen); (void) strlcat(buf, tbl[i].name, buflen); n++; } if (n == 0) (void) strlcat(buf, "-", buflen); } static void print_perms(struct flag_tbl *tbl, size_t nent, uint32_t val, char *buf, uint_t buflen) { uint_t n = 0; uint_t i; if (opt_x) { (void) snprintf(buf, buflen, "%" PRIx32, val); return; } for (i = 0; i < nent; i++) { if ((val & tbl[i].flag) == 0) { (void) strlcat(buf, "-", buflen); } else { (void) strlcat(buf, tbl[i].name, buflen); } n++; } } __NORETURN static void ofmt_fatal(ofmt_handle_t hdl, ofmt_field_t *templ, ofmt_status_t status) { char buf[OFMT_BUFSIZE]; char *msg = ofmt_strerror(hdl, status, buf, sizeof (buf)); warnx(_("ofmt error: %s"), msg); if (status == OFMT_EBADFIELDS || status == OFMT_ENOFIELDS) { ofmt_field_t *f = templ; fprintf(stderr, _("Valid fields are: ")); while (f->of_name != NULL) { fprintf(stderr, "%s", f->of_name); f++; if (f->of_name != NULL) fprintf(stderr, ","); } fprintf(stderr, "\n"); } exit(EXIT_FAILURE); } __NORETURN static void fatal(const char *msg, ...) { char buf[128]; va_list ap; size_t len; va_start(ap, msg); (void) vsnprintf(buf, sizeof (buf), msg, ap); va_end(ap); len = strlen(buf); upanic(buf, len); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2014 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2018, Joyent, Inc. # PROG= smbd OBJS= \ smbd_authsvc.o \ smbd_doorsvc.o \ smbd_join.o \ smbd_krb5lookup.o \ smbd_krb5ssp.o \ smbd_logon.o \ smbd_main.o \ smbd_nicmon.o \ smbd_ntlmssp.o \ smbd_pipesvc.o \ smbd_share_doorsvc.o \ smbd_spool.o \ smbd_syslog.o \ smbd_vss.o SRCS= $(OBJS:%.o=%.c) include ../../Makefile.cmd include ../../Makefile.ctf # Hammerhead: Include mech_krb5 definitions for direct linking. # libkrb5.so is a filter library; GNU ld cannot resolve symbols through it. include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 MANIFEST= server.xml SVCMETHOD= svc-smbd LOGDLL= eventlog.dll ROOTMANIFESTDIR = $(ROOTSVCSMB) ROOTVARSMBDLLDIR = $(ROOTVARSMB)/cvol/windows/system32 ROOTVARSMBDLL= $(LOGDLL:%=$(ROOTVARSMBDLLDIR)/%) $(ROOTMANIFEST): FILEMODE = 0444 $(ROOTSVCMETHOD): FILEMODE = 0555 $(ROOTVARSMBDLL): FILEMODE = 0755 CFLAGS += $(CCVERBOSE) CPPFLAGS += -D_IPP_PRIVATE_STRUCTURES CPPFLAGS += -D_REENTRANT CPPFLAGS += -Dsyslog=smb_syslog $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG # Should not have to do this, but the Kerberos includes are a mess. CPPFLAGS += -I $(ROOT)/usr/include/kerberosv5 # needs work SMOFF += all_func_returns,deref_check # Hammerhead: Suppress pointer/int cast warnings in legacy code CERRWARN += -Wno-pointer-to-int-cast CERRWARN += -Wno-int-to-pointer-cast CSTD = $(CSTD_GNU99) LDFLAGS += -R/usr/lib/smbsrv LDLIBS += -L$(ROOT)/usr/lib/smbsrv # prefer to keep libs ordered by dependence LDLIBS += -lmlsvc -lmlrpc -lsmbns -lsmb -lsmbfs -lgss LDLIBS += -lzfs -lbsm -lscf -lcmdutils -lsocket -lnsl -lumem # Hammerhead: Link directly against mech_krb5.so instead of filter lib -lkrb5 $(PROG) : LDLIBS += $(KMECHLIB) $(PROG) : LDFLAGS += $(KRUNPATH) # Hammerhead: converted from dmake conditional ifneq ($(ENABLE_SMB_PRINTING),$(POUND_SIGN)) CPPFLAGS += -DHAVE_CUPS endif ROOTSMBDDIR = $(ROOTLIB)/smbsrv ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%) FILEMODE = 0444 $(ROOTSMBDFILE): FILEMODE = 0555 $(ROOTSMBDDIR)/%: % $(INS.file) $(ROOTVARSMBDLLDIR)/%: % $(INS.file) all: $(PROG) clean: $(RM) $(OBJS) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) check: $(CHKMANIFEST) _msg: include ../../Makefile.targ install: all .WAIT $(ROOTETCDEFAULTFILES) $(ROOTMANIFEST) \ $(ROOTSMBDFILE) ${ROOTSVCMETHOD} $(ROOTVARSMBDLL) .KEEP_STATE: MZÿÿ¸@°º´ Í!¸LÍ!This program cannot be run in DOS mode. $½ÈWÙù©9Šù©9Šù©9Š4EŠø©9Š4AŠø©9ŠRichù©9ŠPEL%ŒKà! 0Ø .rsrcØ@@.reloc @B €0€ H`t4@@D€€TÀÀd%1 %1 %1 %1  /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2022 Tintri by DDN, Inc. All rights reserved. * Copyright 2022-2023 RackTop Systems, Inc. */ #ifndef _SMBD_H #define _SMBD_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include void smbd_nomem(void) __NORETURN; void smbd_report(const char *fmt, ...); int smbd_pipesvc_start(void); void smbd_pipesvc_stop(void); int smbd_share_start(void); void smbd_share_stop(void); int smbd_nicmon_start(const char *); void smbd_nicmon_stop(void); int smbd_nicmon_refresh(void); int smbd_dc_monitor_init(void); void smbd_dc_monitor_refresh(void); smb_token_t *smbd_user_auth_logon(smb_logon_t *); void smbd_user_nonauth_logon(uint32_t); void smbd_user_auth_logoff(uint32_t); void smbd_join(smb_joininfo_t *, smb_joinres_t *); void smbd_set_secmode(int); boolean_t smbd_online(void); void smbd_online_wait(const char *); void smbd_get_authconf(smb_kmod_cfg_t *); uint32_t smbd_logon_final(smb_token_t *, smb_inaddr_t *, char *, char *, uint32_t); void smbd_spool_start(void); void smbd_spool_stop(void); int smbd_cups_init(void); void smbd_cups_fini(void); void smbd_load_printers(void); int smbd_vss_get_count(const char *, uint32_t *); void smbd_vss_get_snapshots(const char *, uint32_t, uint32_t *, uint32_t *, char **); int smbd_vss_map_gmttoken(const char *, char *, time_t, char *); typedef struct smbd { const char *s_version; /* smbd version string */ const char *s_pname; /* basename to use for messages */ pid_t s_pid; /* process-ID of current daemon */ uid_t s_uid; /* UID of current daemon */ gid_t s_gid; /* GID of current daemon */ int s_fg; /* Run in foreground */ int s_debug; /* Enable debug output */ int s_dbg_stop; /* stop for debugger attach */ boolean_t s_initialized; boolean_t s_shutting_down; /* shutdown control */ volatile uint_t s_refreshes; boolean_t s_kbound; /* B_TRUE if bound to kernel */ int s_authsvc_sock; int s_door_lmshr; int s_door_srv; int s_door_opipe; int s_secmode; /* Current security mode */ boolean_t s_dc_changed; pthread_t s_refresh_tid; pthread_t s_authsvc_tid; pthread_t s_localtime_tid; pthread_t s_spool_tid; pthread_t s_dc_monitor_tid; boolean_t s_nbt_listener_running; boolean_t s_tcp_listener_running; pthread_t s_nbt_listener_id; pthread_t s_tcp_listener_id; boolean_t s_fatal_error; } smbd_t; extern smbd_t smbd; #define SMBD_LOG_MSGSIZE 256 #define SMBD_DOOR_NAMESZ 16 typedef struct smbd_door { mutex_t sd_mutex; cond_t sd_cv; uint32_t sd_ncalls; char sd_name[SMBD_DOOR_NAMESZ]; } smbd_door_t; #define SMBD_ARG_MAGIC 0x53415247 /* 'SARG' */ /* * Parameter for door operations. */ typedef struct smbd_arg { uint32_t magic; list_node_t lnd; smb_doorhdr_t hdr; const char *opname; char *data; size_t datalen; char *rbuf; size_t rsize; boolean_t response_ready; boolean_t response_abort; uint32_t status; } smbd_arg_t; int smbd_door_start(void); void smbd_door_stop(void); void smbd_door_init(smbd_door_t *, const char *); void smbd_door_fini(smbd_door_t *); void smbd_door_enter(smbd_door_t *); void smbd_door_return(smbd_door_t *, char *, size_t, door_desc_t *, uint_t); void *smbd_door_dispatch_op(void *); int smbd_authsvc_start(void); void smbd_authsvc_stop(void); /* For fksmbd */ void fksmbd_init(void); int fksmbd_door_dispatch(smb_doorarg_t *); #ifdef __cplusplus } #endif #endif /* _SMBD_H */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. * Copyright 2022-2023 RackTop Systems, Inc. */ /* * SMB authentication service * * This service listens on a local AF_UNIX socket, spawning a * thread to service each connection. The client-side of such * connections is the in-kernel SMB service, with an open and * connect done in the SMB session setup handler. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" #include "smbd_authsvc.h" /* Arbitrary value outside the (small) range of valid OIDs */ #define special_mech_raw_NTLMSSP (spnego_mech_oid_NTLMSSP + 100) static struct sockaddr_un smbauth_sockname = { AF_UNIX, SMB_AUTHSVC_SOCKNAME }; typedef struct spnego_mech_handler { int mh_oid; /* SPNEGO_MECH_OID */ int (*mh_init)(authsvc_context_t *); int (*mh_work)(authsvc_context_t *); void (*mh_fini)(authsvc_context_t *); } spnego_mech_handler_t; static int smbd_authsock_create(void); static void smbd_authsock_destroy(void); static void *smbd_authsvc_listen(void *); static void *smbd_authsvc_work(void *); static void smbd_authsvc_flood(void); static int smbd_authsvc_oldreq(authsvc_context_t *); static int smbd_authsvc_clinfo(authsvc_context_t *); static int smbd_authsvc_esfirst(authsvc_context_t *); static int smbd_authsvc_esnext(authsvc_context_t *); static int smbd_authsvc_escmn(authsvc_context_t *); static int smbd_authsvc_newmech(authsvc_context_t *); static int smbd_authsvc_gettoken(authsvc_context_t *); static int smbd_raw_ntlmssp_esfirst(authsvc_context_t *); static int smbd_raw_ntlmssp_esnext(authsvc_context_t *); /* * We can get relatively large tokens now, thanks to krb5 PAC. * Might be better to size these buffers dynamically, but these * are all short-lived so not bothering with that for now. */ int smbd_authsvc_bufsize = 65000; static mutex_t smbd_authsvc_mutex = DEFAULTMUTEX; /* * The maximum number of authentication thread is limited by the * smbsrv smb_threshold_...(->sv_ssetup_ct) mechanism. However, * due to occasional delays closing these auth. sockets, we need * a little "slack" on the number of threads we'll allow, as * compared with the in-kernel limit. We could perhaps just * remove this limit now, but want it for extra safety. */ int smbd_authsvc_maxthread = SMB_AUTHSVC_MAXTHREAD + 32; int smbd_authsvc_thrcnt = 0; /* current thrcnt */ int smbd_authsvc_hiwat = 0; /* largest thrcnt seen */ #ifdef DEBUG int smbd_authsvc_slowdown = 0; #endif /* * These are the mechanisms we support, in order of preference. * But note: it's really the _client's_ preference that matters. * See &pref in the spnegoIsMechTypeAvailable() calls below. * Careful with this table; the code below knows its format and * may skip the fist two entries to omit Kerberos. */ static const spnego_mech_handler_t mech_table[] = { { spnego_mech_oid_Kerberos_V5, smbd_krb5ssp_init, smbd_krb5ssp_work, smbd_krb5ssp_fini }, { spnego_mech_oid_Kerberos_V5_Legacy, smbd_krb5ssp_init, smbd_krb5ssp_work, smbd_krb5ssp_fini }, #define MECH_TBL_IDX_NTLMSSP 2 { spnego_mech_oid_NTLMSSP, smbd_ntlmssp_init, smbd_ntlmssp_work, smbd_ntlmssp_fini }, { /* end marker */ spnego_mech_oid_NotUsed, NULL, NULL, NULL }, }; static const spnego_mech_handler_t smbd_auth_mech_raw_ntlmssp = { special_mech_raw_NTLMSSP, smbd_ntlmssp_init, smbd_ntlmssp_work, smbd_ntlmssp_fini }; /* * Start the authentication service. * Returns non-zero on error. */ int smbd_authsvc_start(void) { pthread_attr_t attr; pthread_t tid; int rc; rc = smbd_authsock_create(); if (rc) return (rc); (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&tid, &attr, smbd_authsvc_listen, &smbd); (void) pthread_attr_destroy(&attr); if (rc) { smbd_authsock_destroy(); return (rc); } smbd.s_authsvc_tid = tid; return (0); } void smbd_authsvc_stop(void) { if (smbd.s_authsvc_tid != 0) { (void) pthread_kill(smbd.s_authsvc_tid, SIGTERM); smbd.s_authsvc_tid = 0; } } static int smbd_authsock_create(void) { int sock = -1; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { smbd_report("authsvc, socket create failed, %d", errno); return (errno); } (void) unlink(smbauth_sockname.sun_path); if (bind(sock, (struct sockaddr *)&smbauth_sockname, sizeof (smbauth_sockname)) < 0) { smbd_report("authsvc, socket bind failed, %d", errno); (void) close(sock); return (errno); } if (listen(sock, SOMAXCONN) < 0) { smbd_report("authsvc, socket listen failed, %d", errno); (void) close(sock); return (errno); } smbd.s_authsvc_sock = sock; return (0); } static void smbd_authsock_destroy(void) { int fid; if ((fid = smbd.s_authsvc_sock) != -1) { smbd.s_authsvc_sock = -1; (void) close(fid); } } #ifndef FKSMBD /* * Decide whether to communicate with the client on this AF_UNIX socket. * Normally the caller should be the (in-kernel) SMB service which has * (typically) all privileges. We test for PRIV_SYS_SMB here, which * only the SMB service should have. */ static boolean_t authsock_has_priv(int sock) { ucred_t *uc = NULL; const priv_set_t *ps = NULL; boolean_t ret = B_FALSE; pid_t clpid; if (getpeerucred(sock, &uc) != 0) { smbd_report("authsvc: getpeerucred err %d", errno); return (B_FALSE); } clpid = ucred_getpid(uc); ps = ucred_getprivset(uc, PRIV_EFFECTIVE); if (ps == NULL) { smbd_report("authsvc: ucred_getprivset failed"); goto out; } /* * Require sys_smb priv. */ if (priv_ismember(ps, PRIV_SYS_SMB)) { ret = B_TRUE; goto out; } if (smbd.s_debug) { smbd_report("authsvc: non-privileged client " "PID = %d UID = %d", (int)clpid, ucred_getruid(uc)); } out: /* ps is free'd with the ucred */ if (uc != NULL) ucred_free(uc); return (ret); } #endif static void * smbd_authsvc_listen(void *arg) { authsvc_context_t *ctx; pthread_attr_t attr; pthread_t tid; socklen_t slen; int ls, ns, rc; _NOTE(ARGUNUSED(arg)) (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ls = smbd.s_authsvc_sock; for (;;) { slen = 0; ns = accept(ls, NULL, &slen); if (ns < 0) { switch (errno) { case ECONNABORTED: continue; case EINTR: /* normal termination */ goto out; default: smbd_report("authsvc, socket accept failed," " %d", errno); goto out; } } #ifndef FKSMBD if (!authsock_has_priv(ns)) { close(ns); continue; } #endif /* * Limit the number of auth. sockets * (and the threads that service them). */ (void) mutex_lock(&smbd_authsvc_mutex); if (smbd_authsvc_thrcnt >= smbd_authsvc_maxthread) { (void) mutex_unlock(&smbd_authsvc_mutex); (void) close(ns); smbd_authsvc_flood(); continue; } smbd_authsvc_thrcnt++; if (smbd_authsvc_hiwat < smbd_authsvc_thrcnt) smbd_authsvc_hiwat = smbd_authsvc_thrcnt; (void) mutex_unlock(&smbd_authsvc_mutex); ctx = smbd_authctx_create(); if (ctx == NULL) { smbd_report("authsvc, can't allocate context"); (void) mutex_lock(&smbd_authsvc_mutex); smbd_authsvc_thrcnt--; (void) mutex_unlock(&smbd_authsvc_mutex); (void) close(ns); smbd_nomem(); } ctx->ctx_socket = ns; rc = pthread_create(&tid, &attr, smbd_authsvc_work, ctx); if (rc) { smbd_report("authsvc, thread create failed, %d", rc); (void) mutex_lock(&smbd_authsvc_mutex); smbd_authsvc_thrcnt--; (void) mutex_unlock(&smbd_authsvc_mutex); smbd_authctx_destroy(ctx); ctx = NULL; smbd_nomem(); } ctx = NULL; /* given to the new thread or destroyed */ (void) pthread_detach(tid); } out: (void) pthread_attr_destroy(&attr); smbd_authsock_destroy(); return (NULL); } static void smbd_authsvc_flood(void) { static uint_t count; static time_t last_report; time_t now = time(NULL); count++; if (last_report + 60 < now) { last_report = now; smbd_report("authsvc: flooded %u", count); count = 0; } } authsvc_context_t * smbd_authctx_create(void) { authsvc_context_t *ctx; ctx = malloc(sizeof (*ctx)); if (ctx == NULL) return (NULL); bzero(ctx, sizeof (*ctx)); ctx->ctx_irawlen = smbd_authsvc_bufsize; ctx->ctx_irawbuf = malloc(ctx->ctx_irawlen); ctx->ctx_orawlen = smbd_authsvc_bufsize; ctx->ctx_orawbuf = malloc(ctx->ctx_orawlen); if (ctx->ctx_irawbuf == NULL || ctx->ctx_orawbuf == NULL) goto errout; ctx->ctx_ibodylen = smbd_authsvc_bufsize; ctx->ctx_ibodybuf = malloc(ctx->ctx_ibodylen); ctx->ctx_obodylen = smbd_authsvc_bufsize; ctx->ctx_obodybuf = malloc(ctx->ctx_obodylen); if (ctx->ctx_ibodybuf == NULL || ctx->ctx_obodybuf == NULL) goto errout; return (ctx); errout: smbd_authctx_destroy(ctx); return (NULL); } void smbd_authctx_destroy(authsvc_context_t *ctx) { if (ctx->ctx_socket != -1) { (void) close(ctx->ctx_socket); ctx->ctx_socket = -1; } if (ctx->ctx_token != NULL) smb_token_destroy(ctx->ctx_token); if (ctx->ctx_itoken != NULL) spnegoFreeData(ctx->ctx_itoken); if (ctx->ctx_otoken != NULL) spnegoFreeData(ctx->ctx_otoken); free(ctx->ctx_irawbuf); free(ctx->ctx_orawbuf); free(ctx->ctx_ibodybuf); free(ctx->ctx_obodybuf); free(ctx); } /* * Limit how long smbd_authsvc_work will wait for the client to * send us the next part of the authentication sequence. */ static struct timeval recv_tmo = { 30, 0 }; /* * Also set a timeout for send, where we're sending a response to * the client side (in smbsrv). That should always be waiting in * recv by the time we send, so a short timeout is OK. */ static struct timeval send_tmo = { 15, 0 }; static void * smbd_authsvc_work(void *arg) { authsvc_context_t *ctx = arg; smb_lsa_msg_hdr_t hdr; int sock = ctx->ctx_socket; int len, rc; if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&send_tmo, sizeof (send_tmo)) != 0) { smbd_report("authsvc_work: set set timeout: %m"); goto out; } if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&recv_tmo, sizeof (recv_tmo)) != 0) { smbd_report("authsvc_work: set recv timeout: %m"); goto out; } for (;;) { len = recv(sock, &hdr, sizeof (hdr), MSG_WAITALL); if (len <= 0) { /* normal termination */ break; } if (len != sizeof (hdr)) { smbd_report("authsvc_work: read header failed"); break; } if (hdr.lmh_msglen > smbd_authsvc_bufsize) { smbd_report("authsvc_work: msg too large"); break; } if (hdr.lmh_msglen > 0) { len = recv(sock, ctx->ctx_irawbuf, hdr.lmh_msglen, MSG_WAITALL); if (len != hdr.lmh_msglen) { smbd_report("authsvc_work: read mesg failed"); break; } } ctx->ctx_irawtype = hdr.lmh_msgtype; ctx->ctx_irawlen = hdr.lmh_msglen; ctx->ctx_orawlen = smbd_authsvc_bufsize; ctx->ctx_ibodylen = smbd_authsvc_bufsize; ctx->ctx_obodylen = smbd_authsvc_bufsize; /* * The real work happens here. */ rc = smbd_authsvc_dispatch(ctx); if (rc) break; hdr.lmh_msgtype = ctx->ctx_orawtype; hdr.lmh_msglen = ctx->ctx_orawlen; len = send(sock, &hdr, sizeof (hdr), 0); if (len != sizeof (hdr)) { smbd_report("authsvc_work: send failed"); break; } if (ctx->ctx_orawlen > 0) { len = send(sock, ctx->ctx_orawbuf, ctx->ctx_orawlen, 0); if (len != ctx->ctx_orawlen) { smbd_report("authsvc_work: send failed"); break; } } } out: if (ctx->ctx_mh_fini) (ctx->ctx_mh_fini)(ctx); smbd_authctx_destroy(ctx); (void) mutex_lock(&smbd_authsvc_mutex); smbd_authsvc_thrcnt--; (void) mutex_unlock(&smbd_authsvc_mutex); return (NULL); /* implied pthread_exit() */ } /* * Dispatch based on message type LSA_MTYPE_... * Non-zero return here ends the conversation. */ int smbd_authsvc_dispatch(authsvc_context_t *ctx) { int rc; switch (ctx->ctx_irawtype) { case LSA_MTYPE_OLDREQ: #ifdef DEBUG if (smbd_authsvc_slowdown) (void) sleep(smbd_authsvc_slowdown); #endif rc = smbd_authsvc_oldreq(ctx); break; case LSA_MTYPE_CLINFO: rc = smbd_authsvc_clinfo(ctx); break; case LSA_MTYPE_ESFIRST: rc = smbd_authsvc_esfirst(ctx); break; case LSA_MTYPE_ESNEXT: #ifdef DEBUG if (smbd_authsvc_slowdown) (void) sleep(smbd_authsvc_slowdown); #endif rc = smbd_authsvc_esnext(ctx); break; case LSA_MTYPE_GETTOK: rc = smbd_authsvc_gettoken(ctx); break; /* response types */ case LSA_MTYPE_OK: case LSA_MTYPE_ERROR: case LSA_MTYPE_TOKEN: case LSA_MTYPE_ES_CONT: case LSA_MTYPE_ES_DONE: default: return (-1); } if (rc == NT_STATUS_NO_MEMORY) smbd_nomem(); if (rc != 0) { smb_lsa_eresp_t *er = ctx->ctx_orawbuf; ctx->ctx_orawtype = LSA_MTYPE_ERROR; ctx->ctx_orawlen = sizeof (*er); er->ler_ntstatus = rc; er->ler_errclass = 0; er->ler_errcode = 0; } return (0); } static int smbd_authsvc_oldreq(authsvc_context_t *ctx) { smb_logon_t user_info; XDR xdrs; smb_token_t *token = NULL; int rc = 0; bzero(&user_info, sizeof (user_info)); xdrmem_create(&xdrs, ctx->ctx_irawbuf, ctx->ctx_irawlen, XDR_DECODE); if (!smb_logon_xdr(&xdrs, &user_info)) { xdr_destroy(&xdrs); return (NT_STATUS_INVALID_PARAMETER); } xdr_destroy(&xdrs); token = smbd_user_auth_logon(&user_info); xdr_free(smb_logon_xdr, (char *)&user_info); if (token == NULL) { rc = user_info.lg_status; if (rc == 0) /* should not happen */ rc = NT_STATUS_INTERNAL_ERROR; return (rc); } ctx->ctx_token = token; return (rc); } static int smbd_authsvc_clinfo(authsvc_context_t *ctx) { if (ctx->ctx_irawlen != sizeof (smb_lsa_clinfo_t)) return (NT_STATUS_INTERNAL_ERROR); (void) memcpy(&ctx->ctx_clinfo, ctx->ctx_irawbuf, sizeof (smb_lsa_clinfo_t)); ctx->ctx_orawtype = LSA_MTYPE_OK; ctx->ctx_orawlen = 0; return (0); } /* * Handle a security blob we've received from the client. * Incoming type: LSA_MTYPE_ESFIRST * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE, * LSA_MTYPE_ERROR */ static int smbd_authsvc_esfirst(authsvc_context_t *ctx) { const spnego_mech_handler_t *mh; int idx, pref, rc; int best_pref = 1000; int best_mhidx = -1; /* * NTLMSSP header is 8+, SPNEGO is 10+ */ if (ctx->ctx_irawlen < 8) { smbd_report("authsvc: short blob"); return (NT_STATUS_INVALID_PARAMETER); } /* * We could have "Raw NTLMSSP" here intead of SPNEGO. */ if (bcmp(ctx->ctx_irawbuf, "NTLMSSP", 8) == 0) { rc = smbd_raw_ntlmssp_esfirst(ctx); return (rc); } /* * Parse the SPNEGO token, check its type. */ rc = spnegoInitFromBinary(ctx->ctx_irawbuf, ctx->ctx_irawlen, &ctx->ctx_itoken); if (rc != 0) { smbd_report("authsvc: spnego parse failed"); return (NT_STATUS_INVALID_PARAMETER); } rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype); if (rc != 0) { smbd_report("authsvc: spnego get token type failed"); return (NT_STATUS_INVALID_PARAMETER); } if (ctx->ctx_itoktype != SPNEGO_TOKEN_INIT) { smbd_report("authsvc: spnego wrong token type %d", ctx->ctx_itoktype); return (NT_STATUS_INVALID_PARAMETER); } /* * Figure out which mech type to use. We want to use the * first of the client's supported mechanisms that we also * support. Unfortunately, the spnego code does not have an * interface to walk the token's mech list, so we have to * ask about each mech type we know and keep track of which * was earliest in the token's mech list. * * Also, skip the Kerberos mechanisms in workgroup mode. */ idx = 0; mh = mech_table; if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) { idx = MECH_TBL_IDX_NTLMSSP; mh = &mech_table[idx]; } for (; mh->mh_init != NULL; idx++, mh++) { if (spnegoIsMechTypeAvailable(ctx->ctx_itoken, mh->mh_oid, &pref) != 0) continue; if (pref < best_pref) { best_pref = pref; best_mhidx = idx; } } if (best_mhidx == -1) { smbd_report("authsvc: no supported spnego mechanism"); return (NT_STATUS_INVALID_PARAMETER); } /* Found a mutually agreeable mech. */ mh = &mech_table[best_mhidx]; ctx->ctx_mech_oid = mh->mh_oid; ctx->ctx_mh_work = mh->mh_work; ctx->ctx_mh_fini = mh->mh_fini; rc = mh->mh_init(ctx); if (rc != 0) { smbd_report("authsvc: mech init failed"); return (rc); } /* * If the best supported mech was not the first in the list, * we need to ask the client to use a different one, and * skip (ignore) the provided token body. */ if (best_pref != 0) { rc = smbd_authsvc_newmech(ctx); } else { /* * Common to LSA_MTYPE_ESFIRST, LSA_MTYPE_ESNEXT */ rc = smbd_authsvc_escmn(ctx); } return (rc); } /* * Handle a security blob we've received from the client. * Incoming type: LSA_MTYPE_ESNEXT * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE, * LSA_MTYPE_ERROR */ static int smbd_authsvc_esnext(authsvc_context_t *ctx) { int rc; /* * Make sure LSA_MTYPE_ESFIRST was handled * previously, so we have a work function. */ if (ctx->ctx_mh_work == NULL) return (NT_STATUS_INVALID_PARAMETER); if (ctx->ctx_mech_oid == special_mech_raw_NTLMSSP) { rc = smbd_raw_ntlmssp_esnext(ctx); return (rc); } /* * Cleanup state from previous calls. */ if (ctx->ctx_itoken != NULL) { spnegoFreeData(ctx->ctx_itoken); ctx->ctx_itoken = NULL; } /* * Parse the SPNEGO token, check its type. */ rc = spnegoInitFromBinary(ctx->ctx_irawbuf, ctx->ctx_irawlen, &ctx->ctx_itoken); if (rc != 0) return (NT_STATUS_INVALID_PARAMETER); rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype); if (rc != 0) return (NT_STATUS_INVALID_PARAMETER); if (ctx->ctx_itoktype != SPNEGO_TOKEN_TARG) return (NT_STATUS_INVALID_PARAMETER); rc = smbd_authsvc_escmn(ctx); return (rc); } static int smbd_authsvc_escmn(authsvc_context_t *ctx) { SPNEGO_MECH_OID oid; ulong_t toklen; int rc; /* * Cleanup state from previous calls. */ if (ctx->ctx_otoken != NULL) { spnegoFreeData(ctx->ctx_otoken); ctx->ctx_otoken = NULL; } /* * Extract the payload (mech token). */ toklen = ctx->ctx_ibodylen; rc = spnegoGetMechToken(ctx->ctx_itoken, ctx->ctx_ibodybuf, &toklen); switch (rc) { case SPNEGO_E_SUCCESS: break; case SPNEGO_E_ELEMENT_UNAVAILABLE: toklen = 0; break; case SPNEGO_E_BUFFER_TOO_SMALL: return (NT_STATUS_BUFFER_TOO_SMALL); default: return (NT_STATUS_INTERNAL_ERROR); } ctx->ctx_ibodylen = toklen; /* * Now that we have the incoming "body" (mech. token), * call the back-end mech-specific work function to * create the outgoing "body" (mech. token). * * The worker must fill in: ctx->ctx_negresult, * and: ctx->ctx_obodylen, but ctx->ctx_obodybuf * is optional, and is typically NULL after the * final message of an auth sequence, where * negresult == spnego_negresult_complete. */ rc = ctx->ctx_mh_work(ctx); if (rc != 0) return (rc); /* * Wrap the outgoing body in a negTokenTarg SPNEGO token. * The selected mech. OID is returned only when the * incoming token was of type SPNEGO_TOKEN_INIT. */ if (ctx->ctx_itoktype == SPNEGO_TOKEN_INIT) { /* tell the client the selected mech. */ oid = ctx->ctx_mech_oid; } else { /* Omit the "supported mech." field. */ oid = spnego_mech_oid_NotUsed; } /* * Determine the spnego "negresult" from the * reply message type (from the work func). */ switch (ctx->ctx_orawtype) { case LSA_MTYPE_ERROR: ctx->ctx_negresult = spnego_negresult_rejected; break; case LSA_MTYPE_ES_DONE: ctx->ctx_negresult = spnego_negresult_success; break; case LSA_MTYPE_ES_CONT: ctx->ctx_negresult = spnego_negresult_incomplete; break; default: return (-1); } rc = spnegoCreateNegTokenTarg( oid, ctx->ctx_negresult, ctx->ctx_obodybuf, /* may be NULL */ ctx->ctx_obodylen, NULL, 0, &ctx->ctx_otoken); if (rc != 0) return (NT_STATUS_INTERNAL_ERROR); /* * Convert the SPNEGO token into binary form, * writing it to the output buffer. */ toklen = smbd_authsvc_bufsize; rc = spnegoTokenGetBinary(ctx->ctx_otoken, (uchar_t *)ctx->ctx_orawbuf, &toklen); if (rc != 0) rc = NT_STATUS_INTERNAL_ERROR; ctx->ctx_orawlen = (uint_t)toklen; return (rc); } /* * The first NegTokenInit we receive, handled in smbd_authsvc_esfirst, * contains a list of supported mechanisms, in order from the client's * most preferred to least preferred. The token also contains a body * for the first mechanism listed, which is used immediately if the * first mechanism is mutually agreeable. If the first mechanism is * not supported on our side, we must "propose a new mechanism" from * the list. Our caller has selected a mech and initialized the ctx * mech functions. Here compose a reply with an empty body and the * proposed new mechanism OID. The token body received is for some * other mech, so we skip calling the work function with that token. * * Just send a NegTokenTarg with an empty body and the OID for the * proposed mechanism. The next message should be the real first * token for this mechanism, handled in smbd_authsvc_exnext. */ static int smbd_authsvc_newmech(authsvc_context_t *ctx) { ulong_t toklen; int rc; /* * Don't call mh_work here. * Just tell the clint the selected mech. */ ctx->ctx_ibodylen = 0; ctx->ctx_orawtype = LSA_MTYPE_ES_CONT; ctx->ctx_obodylen = 0; ctx->ctx_negresult = spnego_negresult_request_mic; rc = spnegoCreateNegTokenTarg( ctx->ctx_mech_oid, ctx->ctx_negresult, NULL, 0, NULL, 0, &ctx->ctx_otoken); if (rc != 0) return (NT_STATUS_INTERNAL_ERROR); /* * Convert the SPNEGO token into binary form, * writing it to the output buffer. */ toklen = smbd_authsvc_bufsize; rc = spnegoTokenGetBinary(ctx->ctx_otoken, (uchar_t *)ctx->ctx_orawbuf, &toklen); if (rc) rc = NT_STATUS_INTERNAL_ERROR; ctx->ctx_orawlen = (uint_t)toklen; return (rc); } /* * Wrapper for "Raw NTLMSSP", which is exactly like the * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO. * Setup back-end handler for: special_mech_raw_NTLMSSP * Compare with smbd_authsvc_esfirst(). */ static int smbd_raw_ntlmssp_esfirst(authsvc_context_t *ctx) { const spnego_mech_handler_t *mh; int rc; mh = &smbd_auth_mech_raw_ntlmssp; rc = mh->mh_init(ctx); if (rc != 0) return (rc); ctx->ctx_mech_oid = mh->mh_oid; ctx->ctx_mh_work = mh->mh_work; ctx->ctx_mh_fini = mh->mh_fini; rc = smbd_raw_ntlmssp_esnext(ctx); return (rc); } /* * Wrapper for "Raw NTLMSSP", which is exactly like the * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO. * Just copy "raw" to "body", and vice versa. * Compare with smbd_authsvc_esnext, smbd_authsvc_escmn */ static int smbd_raw_ntlmssp_esnext(authsvc_context_t *ctx) { int rc; ctx->ctx_ibodylen = ctx->ctx_irawlen; (void) memcpy(ctx->ctx_ibodybuf, ctx->ctx_irawbuf, ctx->ctx_irawlen); rc = ctx->ctx_mh_work(ctx); ctx->ctx_orawlen = ctx->ctx_obodylen; (void) memcpy(ctx->ctx_orawbuf, ctx->ctx_obodybuf, ctx->ctx_obodylen); return (rc); } /* * After a successful authentication, request the access token. */ static int smbd_authsvc_gettoken(authsvc_context_t *ctx) { XDR xdrs; smb_token_t *token = NULL; int rc = 0; int len; if ((token = ctx->ctx_token) == NULL) return (NT_STATUS_ACCESS_DENIED); /* * Encode the token response */ len = xdr_sizeof(smb_token_xdr, token); if (len > ctx->ctx_orawlen) { if ((ctx->ctx_orawbuf = realloc(ctx->ctx_orawbuf, len)) == NULL) { return (NT_STATUS_NO_MEMORY); } } ctx->ctx_orawtype = LSA_MTYPE_TOKEN; ctx->ctx_orawlen = len; xdrmem_create(&xdrs, ctx->ctx_orawbuf, len, XDR_ENCODE); if (!smb_token_xdr(&xdrs, token)) rc = NT_STATUS_INTERNAL_ERROR; xdr_destroy(&xdrs); return (rc); } /* * Initialization time code to figure out what mechanisms we support. * Careful with this table; the code below knows its format and may * skip the fist two entries to omit Kerberos. */ static SPNEGO_MECH_OID MechTypeList[] = { spnego_mech_oid_Kerberos_V5, spnego_mech_oid_Kerberos_V5_Legacy, #define MECH_OID_IDX_NTLMSSP 2 spnego_mech_oid_NTLMSSP, }; static int MechTypeCnt = sizeof (MechTypeList) / sizeof (MechTypeList[0]); /* This string is just like Windows. */ static char IgnoreSPN[] = "not_defined_in_RFC4178@please_ignore"; /* * Build the SPNEGO "hint" token based on the * configured authentication mechanisms. * (NTLMSSP, and maybe Kerberos) */ void smbd_get_authconf(smb_kmod_cfg_t *kcfg) { SPNEGO_MECH_OID *mechList = MechTypeList; int mechCnt = MechTypeCnt; SPNEGO_TOKEN_HANDLE hSpnegoToken = NULL; uchar_t *pBuf = kcfg->skc_negtok; uint32_t *pBufLen = &kcfg->skc_negtok_len; ulong_t tLen = sizeof (kcfg->skc_negtok); int rc; /* * In workgroup mode, skip Kerberos. */ if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) { mechList += MECH_OID_IDX_NTLMSSP; mechCnt -= MECH_OID_IDX_NTLMSSP; } rc = spnegoCreateNegTokenHint(mechList, mechCnt, (uchar_t *)IgnoreSPN, &hSpnegoToken); if (rc != SPNEGO_E_SUCCESS) { syslog(LOG_DEBUG, "smb_config_get_negtok: " "spnegoCreateNegTokenHint, rc=%d", rc); *pBufLen = 0; return; } rc = spnegoTokenGetBinary(hSpnegoToken, pBuf, &tLen); if (rc != SPNEGO_E_SUCCESS) { syslog(LOG_DEBUG, "smb_config_get_negtok: " "spnegoTokenGetBinary, rc=%d", rc); *pBufLen = 0; } else { *pBufLen = (uint32_t)tLen; } spnegoFreeData(hSpnegoToken); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBD_AUTHSVC_H #define _SMBD_AUTHSVC_H /* * Declarations shared with authsvc modules. */ #include #include /* * This is the common authsvc_context shared by all back-ends. * Note that ctx_mech_oid is really SPNEGO_MECH_OID, and the * ctx_itoken, ctx_otoken members are SPNEGO_TOKEN_HANDLE, * but this is using the underlying types so as to avoid * dragging in spnego.h here. */ typedef struct authsvc_context { int ctx_socket; int ctx_mech_oid; int (*ctx_mh_work)(struct authsvc_context *); void (*ctx_mh_fini)(struct authsvc_context *); int ctx_itoktype; int ctx_negresult; /* (in,out) SPNEGO token handles */ void *ctx_itoken; void *ctx_otoken; /* (in,out) raw (buf,len,type) */ void *ctx_irawbuf; uint_t ctx_irawlen; int ctx_irawtype; void *ctx_orawbuf; uint_t ctx_orawlen; int ctx_orawtype; /* (in,out) body (buf,len) */ void *ctx_ibodybuf; uint_t ctx_ibodylen; void *ctx_obodybuf; uint_t ctx_obodylen; /* who is the client */ smb_lsa_clinfo_t ctx_clinfo; /* final authentication token */ struct smb_token *ctx_token; /* private data for the back-end */ void *ctx_backend; } authsvc_context_t; int smbd_krb5ssp_init(authsvc_context_t *); int smbd_krb5ssp_work(authsvc_context_t *); void smbd_krb5ssp_fini(authsvc_context_t *); int smbd_ntlmssp_init(authsvc_context_t *); int smbd_ntlmssp_work(authsvc_context_t *); void smbd_ntlmssp_fini(authsvc_context_t *); /* Exposed for unit tests. */ int smbd_authsvc_dispatch(authsvc_context_t *); authsvc_context_t *smbd_authctx_create(void); void smbd_authctx_destroy(authsvc_context_t *); #endif /* _SMBD_AUTHSVC_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2019 Nexenta Systems, Inc. All rights reserved. * Copyright 2022 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* DAEMON_UID */ #include #include #include #include #include #include "smbd.h" /* * The list contains asynchronous requests that have been initiated * but have not yet been collected (via smbd_dop_async_response). */ typedef struct smbd_doorsvc { mutex_t sd_mutex; cond_t sd_cv; list_t sd_async_list; uint32_t sd_async_count; } smbd_doorsvc_t; static int smbd_dop_null(smbd_arg_t *); static int smbd_dop_async_response(smbd_arg_t *); static int smbd_dop_user_auth_logon(smbd_arg_t *); static int smbd_dop_user_nonauth_logon(smbd_arg_t *); static int smbd_dop_user_auth_logoff(smbd_arg_t *); static int smbd_dop_lookup_sid(smbd_arg_t *); static int smbd_dop_lookup_name(smbd_arg_t *); static int smbd_dop_join(smbd_arg_t *); static int smbd_dop_get_dcinfo(smbd_arg_t *); static int smbd_dop_vss_get_count(smbd_arg_t *); static int smbd_dop_vss_get_snapshots(smbd_arg_t *); static int smbd_dop_vss_map_gmttoken(smbd_arg_t *); static int smbd_dop_ads_find_host(smbd_arg_t *); static int smbd_dop_quota_query(smbd_arg_t *); static int smbd_dop_quota_set(smbd_arg_t *); static int smbd_dop_dfs_get_referrals(smbd_arg_t *); static int smbd_dop_shr_hostaccess(smbd_arg_t *); static int smbd_dop_shr_exec(smbd_arg_t *); static int smbd_dop_notify_dc_changed(smbd_arg_t *); typedef int (*smbd_dop_t)(smbd_arg_t *); typedef struct smbd_doorop { smb_dopcode_t opcode; int opflags; smbd_dop_t op; } smbd_doorop_t; /* doorop opflags */ #define DOF_R 1 /* read */ #define DOF_W 2 /* write */ #define DOF_K 4 /* kernel caller */ #define DOF_D 8 /* daemon caller */ smbd_doorop_t smbd_doorops[] = { { SMB_DR_NULL, DOF_R, smbd_dop_null }, { SMB_DR_ASYNC_RESPONSE, DOF_K, smbd_dop_async_response }, { SMB_DR_USER_AUTH_LOGON, DOF_W, smbd_dop_user_auth_logon }, { SMB_DR_USER_NONAUTH_LOGON, DOF_W, smbd_dop_user_nonauth_logon }, { SMB_DR_USER_AUTH_LOGOFF, DOF_K, smbd_dop_user_auth_logoff }, { SMB_DR_LOOKUP_SID, DOF_R, smbd_dop_lookup_sid }, { SMB_DR_LOOKUP_NAME, DOF_R, smbd_dop_lookup_name }, { SMB_DR_JOIN, DOF_W, smbd_dop_join }, { SMB_DR_GET_DCINFO, DOF_R, smbd_dop_get_dcinfo }, { SMB_DR_VSS_GET_COUNT, DOF_K, smbd_dop_vss_get_count }, { SMB_DR_VSS_GET_SNAPSHOTS, DOF_K, smbd_dop_vss_get_snapshots }, { SMB_DR_VSS_MAP_GMTTOKEN, DOF_K, smbd_dop_vss_map_gmttoken }, { SMB_DR_ADS_FIND_HOST, DOF_R, smbd_dop_ads_find_host }, { SMB_DR_QUOTA_QUERY, DOF_K, smbd_dop_quota_query }, { SMB_DR_QUOTA_SET, DOF_K, smbd_dop_quota_set }, { SMB_DR_DFS_GET_REFERRALS, DOF_K, smbd_dop_dfs_get_referrals }, { SMB_DR_SHR_HOSTACCESS, DOF_K, smbd_dop_shr_hostaccess }, { SMB_DR_SHR_EXEC, DOF_K, smbd_dop_shr_exec }, { SMB_DR_NOTIFY_DC_CHANGED, DOF_W|DOF_D, smbd_dop_notify_dc_changed }, { SMB_DR_LOOKUP_LSID, DOF_R, smbd_dop_lookup_sid }, { SMB_DR_LOOKUP_LNAME, DOF_R, smbd_dop_lookup_name } }; static int smbd_ndoorop = (sizeof (smbd_doorops) / sizeof (smbd_doorops[0])); static smbd_doorsvc_t smbd_doorsvc; static int smbd_door_fd = -1; static int smbd_door_cookie = 0x534D4244; /* SMBD */ static smbd_door_t smbd_door_sdh; static char *smbd_door_name = NULL; static void smbd_door_dispatch(void *, char *, size_t, door_desc_t *, uint_t); static int smbd_door_dispatch_async(smbd_arg_t *); static void smbd_door_release_async(smbd_arg_t *); /* * Start the smbd door service. Create and bind to a door. * Returns 0 on success. Otherwise, -1. */ int smbd_door_start(void) { int newfd; (void) mutex_lock(&smbd_doorsvc.sd_mutex); if (smbd_door_fd != -1) { (void) fprintf(stderr, "smb_doorsrv_start: already started"); (void) mutex_unlock(&smbd_doorsvc.sd_mutex); return (-1); } smbd_door_name = getenv("SMBD_DOOR_NAME"); if (smbd_door_name == NULL) smbd_door_name = SMBD_DOOR_NAME; smbd_door_init(&smbd_door_sdh, "doorsrv"); list_create(&smbd_doorsvc.sd_async_list, sizeof (smbd_arg_t), offsetof(smbd_arg_t, lnd)); smbd_doorsvc.sd_async_count = 0; if ((smbd_door_fd = door_create(smbd_door_dispatch, &smbd_door_cookie, DOOR_UNREF)) < 0) { (void) fprintf(stderr, "smb_doorsrv_start: door_create: %s", strerror(errno)); smbd_door_fd = -1; (void) mutex_unlock(&smbd_doorsvc.sd_mutex); return (-1); } (void) unlink(smbd_door_name); if ((newfd = creat(smbd_door_name, 0644)) < 0) { (void) fprintf(stderr, "smb_doorsrv_start: open: %s", strerror(errno)); (void) door_revoke(smbd_door_fd); smbd_door_fd = -1; (void) mutex_unlock(&smbd_doorsvc.sd_mutex); return (-1); } (void) close(newfd); (void) fdetach(smbd_door_name); if (fattach(smbd_door_fd, smbd_door_name) < 0) { (void) fprintf(stderr, "smb_doorsrv_start: fattach: %s", strerror(errno)); (void) door_revoke(smbd_door_fd); smbd_door_fd = -1; (void) mutex_unlock(&smbd_doorsvc.sd_mutex); return (-1); } (void) mutex_unlock(&smbd_doorsvc.sd_mutex); return (smbd_door_fd); } /* * Stop the smbd door service. */ void smbd_door_stop(void) { (void) mutex_lock(&smbd_doorsvc.sd_mutex); smbd_door_fini(&smbd_door_sdh); if (smbd_door_name) (void) fdetach(smbd_door_name); if (smbd_door_fd != -1) { (void) door_revoke(smbd_door_fd); smbd_door_fd = -1; } (void) mutex_unlock(&smbd_doorsvc.sd_mutex); } static boolean_t have_req_privs(uint32_t opcode, int opflags) { ucred_t *uc = NULL; const priv_set_t *ps = NULL; boolean_t ret = B_FALSE; pid_t pid; uid_t uid; /* If only DOF_R (read), let 'em through */ if ((opflags & ~DOF_R) == 0) return (B_TRUE); if (door_ucred(&uc) != 0) { syslog(LOG_DEBUG, "%s: door_ucred failed", __func__); goto out; } /* * in-kernel callers have pid==0 * If we have pid zero, that's sufficient. * If not, allow with sys_smb priv (below) */ pid = ucred_getpid(uc); if ((opflags & DOF_K) != 0) { if (pid == 0) { ret = B_TRUE; goto out; } } uid = ucred_geteuid(uc); if ((opflags & DOF_D) != 0) { if (uid == DAEMON_UID) { ret = B_TRUE; goto out; } } ps = ucred_getprivset(uc, PRIV_EFFECTIVE); if (ps == NULL) { syslog(LOG_DEBUG, "%s: ucred_getprivset failed", __func__); goto out; } if ((opflags & (DOF_W | DOF_K)) != 0) { /* * Modifying operation. Require sys_smb priv. */ if (priv_ismember(ps, PRIV_SYS_SMB)) { ret = B_TRUE; goto out; } } syslog(LOG_DEBUG, "smbd_door_dispatch: missing privilege, " "OpCode = %d PID = %d UID = %d", (int)opcode, (int)pid, (int)uid); out: /* ps is free'd with the ucred */ if (uc != NULL) ucred_free(uc); return (ret); } static smbd_doorop_t * smbd_door_get_opvec(smb_dopcode_t opcode) { smbd_doorop_t *doorop; int i; for (i = 0, doorop = smbd_doorops; i < smbd_ndoorop; i++, doorop++) { if (doorop->opcode == opcode) return (doorop); } return (NULL); } /*ARGSUSED*/ static void smbd_door_dispatch(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, uint_t n_desc) { smbd_arg_t dop_arg; smb_doorhdr_t *hdr; size_t hdr_size; char *rbuf = NULL; smbd_doorop_t *doorop; int opflags; smbd_door_enter(&smbd_door_sdh); if (!smbd_online()) smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0); bzero(&dop_arg, sizeof (smbd_arg_t)); hdr = &dop_arg.hdr; hdr_size = xdr_sizeof(smb_doorhdr_xdr, hdr); if ((cookie != &smbd_door_cookie) || (argp == NULL) || (arg_size < hdr_size)) { smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0); } if (smb_doorhdr_decode(hdr, (uint8_t *)argp, hdr_size) == -1) { syslog(LOG_DEBUG, "smbd_door_dispatch: header decode failed"); smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0); } if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) || (hdr->dh_txid == 0)) { syslog(LOG_DEBUG, "smbd_door_dispatch: invalid header"); smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0); } if ((doorop = smbd_door_get_opvec(hdr->dh_op)) == NULL) { /* invalid door op code */ syslog(LOG_DEBUG, "smbd_door_dispatch: invalid op %u", hdr->dh_op); smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0); } dop_arg.opname = smb_doorhdr_opname(hdr->dh_op); dop_arg.data = argp + hdr_size; dop_arg.datalen = hdr->dh_datalen; /* * Opflags tell us the privileges requried for this op. * Override them for async operations, which are only * requested by the in-kernel door client. */ opflags = doorop->opflags; if ((hdr->dh_flags & SMB_DF_ASYNC) != 0) opflags |= DOF_K; if (!have_req_privs(hdr->dh_op, opflags)) { smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0); } if (hdr->dh_op == SMB_DR_ASYNC_RESPONSE) { /* * ASYNC_RESPONSE is used to collect the response * to an async call; it cannot be an async call. */ hdr->dh_flags &= ~SMB_DF_ASYNC; } if (hdr->dh_flags & SMB_DF_ASYNC) { if (smbd_door_dispatch_async(&dop_arg) == 0) hdr->dh_door_rc = SMB_DOP_SUCCESS; else hdr->dh_door_rc = SMB_DOP_NOT_CALLED; } else { (void) smbd_door_dispatch_op(&dop_arg); } if ((rbuf = (char *)alloca(dop_arg.rsize + hdr_size)) == NULL) { errno = ENOMEM; syslog(LOG_DEBUG, "smbd_door_dispatch[%s]: alloca %m", dop_arg.opname); smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0); } if (dop_arg.rbuf != NULL) { (void) memcpy(rbuf + hdr_size, dop_arg.rbuf, dop_arg.rsize); free(dop_arg.rbuf); } hdr->dh_datalen = dop_arg.rsize; (void) smb_doorhdr_encode(hdr, (uint8_t *)rbuf, hdr_size); dop_arg.rsize += hdr_size; smbd_door_return(&smbd_door_sdh, rbuf, dop_arg.rsize, NULL, 0); /*NOTREACHED*/ } /* * Launch a thread to process an asynchronous door call. */ static int smbd_door_dispatch_async(smbd_arg_t *req_arg) { smbd_arg_t *arg = NULL; char *data = NULL; pthread_attr_t attr; pthread_t tid; int rc; if ((req_arg->hdr.dh_flags & SMB_DF_ASYNC) == 0) { errno = EINVAL; return (-1); } if ((arg = malloc(sizeof (smbd_arg_t))) == NULL) { syslog(LOG_DEBUG, "smbd_door_dispatch_async[%s]: %m", req_arg->opname); return (-1); } (void) memcpy(arg, req_arg, sizeof (smbd_arg_t)); arg->data = NULL; if (req_arg->datalen != 0) { if ((data = malloc(req_arg->datalen)) == NULL) { free(arg); syslog(LOG_DEBUG, "smbd_door_dispatch_async[%s]: %m", req_arg->opname); return (-1); } (void) memcpy(data, req_arg->data, req_arg->datalen); arg->data = data; } (void) mutex_lock(&smbd_doorsvc.sd_mutex); arg->magic = SMBD_ARG_MAGIC; list_insert_tail(&smbd_doorsvc.sd_async_list, arg); ++smbd_doorsvc.sd_async_count; (void) mutex_unlock(&smbd_doorsvc.sd_mutex); (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&tid, &attr, smbd_door_dispatch_op, arg); (void) pthread_attr_destroy(&attr); if (rc != 0) { (void) mutex_lock(&smbd_doorsvc.sd_mutex); smbd_door_release_async(arg); (void) mutex_unlock(&smbd_doorsvc.sd_mutex); } return (rc); } /* * Remove an entry from the async response pending list and free * the arg and associated data. * * Must only be called while holding the smbd_doorsvc mutex. */ static void smbd_door_release_async(smbd_arg_t *arg) { if (arg != NULL) { assert(arg->magic == SMBD_ARG_MAGIC); arg->magic = (uint32_t)~SMBD_ARG_MAGIC; list_remove(&smbd_doorsvc.sd_async_list, arg); --smbd_doorsvc.sd_async_count; free(arg->data); arg->data = NULL; free(arg); } } /* * All door calls are processed here: synchronous or asynchronous: * - synchronous calls are invoked by direct function call * - asynchronous calls are invoked from a launched thread * * If the kernel has attempted to collect a response before the op * has completed, the arg will have been marked as response_abort * and we can discard the response data and release the arg. * * We send a notification when asynchronous (ASYNC) door calls * from the kernel (SYSSPACE) have completed. */ void * smbd_door_dispatch_op(void *thread_arg) { smbd_arg_t *arg = (smbd_arg_t *)thread_arg; smbd_doorop_t *doorop; smb_doorhdr_t *hdr; if ((!smbd_online()) || arg == NULL) return (NULL); hdr = &arg->hdr; arg->opname = smb_doorhdr_opname(hdr->dh_op); doorop = smbd_door_get_opvec(hdr->dh_op); if (doorop == NULL) return (NULL); hdr->dh_door_rc = doorop->op(arg); hdr->dh_status = arg->status; if ((hdr->dh_flags & SMB_DF_SYSSPACE) && (hdr->dh_flags & SMB_DF_ASYNC)) { uint32_t txid = hdr->dh_txid; assert(hdr->dh_op != SMB_DR_ASYNC_RESPONSE); (void) mutex_lock(&smbd_doorsvc.sd_mutex); if (arg->response_abort) { free(arg->rbuf); arg->rbuf = NULL; smbd_door_release_async(arg); } else { arg->response_ready = B_TRUE; } (void) mutex_unlock(&smbd_doorsvc.sd_mutex); (void) smb_kmod_event_notify(txid); } return (NULL); } /* * Wrapper for door_return. smbd_door_enter() increments a reference count * when a door call is dispatched and smbd_door_return() decrements the * reference count when it completes. * * The reference counting is used in smbd_door_fini() to wait for active * calls to complete before closing the door. */ void smbd_door_init(smbd_door_t *sdh, const char *name) { (void) strlcpy(sdh->sd_name, name, sizeof (sdh->sd_name)); } void smbd_door_enter(smbd_door_t *sdh) { (void) mutex_lock(&sdh->sd_mutex); ++sdh->sd_ncalls; (void) mutex_unlock(&sdh->sd_mutex); } /* * We have two calls to door_return because the first call (with data) * can fail, which can leave the door call blocked here. The second * call (with NULL) is guaranteed to unblock and return to the caller. */ void smbd_door_return(smbd_door_t *sdh, char *data_ptr, size_t data_size, door_desc_t *desc_ptr, uint_t num_desc) { (void) mutex_lock(&sdh->sd_mutex); if (sdh->sd_ncalls == 0) syslog(LOG_ERR, "smbd_door_return[%s]: unexpected count=0", sdh->sd_name); else --sdh->sd_ncalls; (void) cond_broadcast(&sdh->sd_cv); (void) mutex_unlock(&sdh->sd_mutex); (void) door_return(data_ptr, data_size, desc_ptr, num_desc); (void) door_return(NULL, 0, NULL, 0); /* NOTREACHED */ } /* * A door service is about to terminate. * Give active requests a small grace period to complete. */ void smbd_door_fini(smbd_door_t *sdh) { timestruc_t delay; int rc = 0; (void) mutex_lock(&sdh->sd_mutex); while (rc != ETIME && sdh->sd_ncalls != 0) { delay.tv_sec = 1; delay.tv_nsec = 0; rc = cond_reltimedwait(&sdh->sd_cv, &sdh->sd_mutex, &delay); } if (sdh->sd_ncalls != 0) syslog(LOG_NOTICE, "smbd_door_fini[%s]: %d remaining", sdh->sd_name, sdh->sd_ncalls); (void) mutex_unlock(&sdh->sd_mutex); } /* * Null door operation: always returns success. * Assumes no request or response data. */ /*ARGSUSED*/ static int smbd_dop_null(smbd_arg_t *arg) { return (SMB_DOP_SUCCESS); } /* * Async response handler: setup the rbuf and rsize for the specified * transaction. This function is used by the kernel to collect the * response half of an asynchronous door call. * * If a door client attempts to collect a response before the op has * completed (!response_ready), mark the arg as response_abort and * set an error. The response will be discarded when the op completes. */ static int smbd_dop_async_response(smbd_arg_t *rsp_arg) { list_t *arg_list = &smbd_doorsvc.sd_async_list; smbd_arg_t *arg; (void) mutex_lock(&smbd_doorsvc.sd_mutex); arg = list_head(arg_list); while (arg != NULL) { assert(arg->magic == SMBD_ARG_MAGIC); if (arg->hdr.dh_txid == rsp_arg->hdr.dh_txid) { if (!arg->response_ready) { arg->response_abort = B_TRUE; rsp_arg->hdr.dh_door_rc = SMB_DOP_NOT_CALLED; syslog(LOG_NOTICE, "doorsvc[%s]: %u not ready", arg->opname, arg->hdr.dh_txid); break; } rsp_arg->rbuf = arg->rbuf; rsp_arg->rsize = arg->rsize; arg->rbuf = NULL; arg->rsize = 0; smbd_door_release_async(arg); break; } arg = list_next(arg_list, arg); } (void) mutex_unlock(&smbd_doorsvc.sd_mutex); return (SMB_DOP_SUCCESS); } static int smbd_dop_user_nonauth_logon(smbd_arg_t *arg) { uint32_t sid = 0; if (smb_common_decode(arg->data, arg->datalen, xdr_uint32_t, &sid) != 0) return (SMB_DOP_DECODE_ERROR); smbd_user_nonauth_logon(sid); return (SMB_DOP_SUCCESS); } static int smbd_dop_user_auth_logoff(smbd_arg_t *arg) { uint32_t sid = 0; if (smb_common_decode(arg->data, arg->datalen, xdr_uint32_t, &sid) != 0) return (SMB_DOP_DECODE_ERROR); smbd_user_auth_logoff(sid); return (SMB_DOP_SUCCESS); } /* * Obtains an access token on successful user authentication. */ static int smbd_dop_user_auth_logon(smbd_arg_t *arg __unused) { /* No longer used */ return (SMB_DOP_EMPTYBUF); } /* * SMB_DR_LOOKUP_NAME, * SMB_DR_LOOKUP_LNAME (local-only, for idmap) */ static int smbd_dop_lookup_name(smbd_arg_t *arg) { smb_domain_t dinfo; smb_account_t ainfo; lsa_account_t acct; char buf[MAXNAMELEN]; bzero(&acct, sizeof (lsa_account_t)); if (smb_common_decode(arg->data, arg->datalen, lsa_account_xdr, &acct) != 0) return (SMB_DOP_DECODE_ERROR); if (*acct.a_domain == '\0') (void) snprintf(buf, MAXNAMELEN, "%s", acct.a_name); else if (strchr(acct.a_domain, '.') != NULL) (void) snprintf(buf, MAXNAMELEN, "%s@%s", acct.a_name, acct.a_domain); else (void) snprintf(buf, MAXNAMELEN, "%s\\%s", acct.a_domain, acct.a_name); switch (arg->hdr.dh_op) { case SMB_DR_LOOKUP_NAME: acct.a_status = lsa_lookup_name(buf, acct.a_sidtype, &ainfo); break; case SMB_DR_LOOKUP_LNAME: /* * Basically for idmap. Don't call out to AD. */ acct.a_status = lsa_lookup_lname(buf, acct.a_sidtype, &ainfo); break; default: assert(!"arg->hdr.dh_op"); acct.a_status = NT_STATUS_INTERNAL_ERROR; break; } if (acct.a_status == NT_STATUS_SUCCESS) { acct.a_sidtype = ainfo.a_type; smb_sid_tostr(ainfo.a_sid, acct.a_sid); (void) strlcpy(acct.a_name, ainfo.a_name, MAXNAMELEN); if (smb_domain_lookup_name(ainfo.a_domain, &dinfo)) (void) strlcpy(acct.a_domain, dinfo.di_fqname, MAXNAMELEN); else (void) strlcpy(acct.a_domain, ainfo.a_domain, MAXNAMELEN); smb_account_free(&ainfo); } arg->rbuf = smb_common_encode(&acct, lsa_account_xdr, &arg->rsize); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } /* * SMB_DR_LOOKUP_SID, * SMB_DR_LOOKUP_LSID (local-only, for idmap) */ static int smbd_dop_lookup_sid(smbd_arg_t *arg) { smb_domain_t dinfo; smb_account_t ainfo; lsa_account_t acct; smb_sid_t *sid; bzero(&acct, sizeof (lsa_account_t)); if (smb_common_decode(arg->data, arg->datalen, lsa_account_xdr, &acct) != 0) return (SMB_DOP_DECODE_ERROR); sid = smb_sid_fromstr(acct.a_sid); switch (arg->hdr.dh_op) { case SMB_DR_LOOKUP_SID: acct.a_status = lsa_lookup_sid(sid, &ainfo); break; case SMB_DR_LOOKUP_LSID: /* * Basically for idmap. Don't call out to AD. */ acct.a_status = lsa_lookup_lsid(sid, &ainfo); break; default: assert(!"arg->hdr.dh_op"); acct.a_status = NT_STATUS_INTERNAL_ERROR; break; } smb_sid_free(sid); if (acct.a_status == NT_STATUS_SUCCESS) { acct.a_sidtype = ainfo.a_type; smb_sid_tostr(ainfo.a_sid, acct.a_sid); (void) strlcpy(acct.a_name, ainfo.a_name, MAXNAMELEN); if (smb_domain_lookup_name(ainfo.a_domain, &dinfo)) (void) strlcpy(acct.a_domain, dinfo.di_fqname, MAXNAMELEN); else (void) strlcpy(acct.a_domain, ainfo.a_domain, MAXNAMELEN); smb_account_free(&ainfo); } arg->rbuf = smb_common_encode(&acct, lsa_account_xdr, &arg->rsize); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } static int smbd_dop_join(smbd_arg_t *arg) { smb_joininfo_t jdi; smb_joinres_t jdres; bzero(&jdi, sizeof (smb_joininfo_t)); bzero(&jdres, sizeof (smb_joinres_t)); if (smb_common_decode(arg->data, arg->datalen, smb_joininfo_xdr, &jdi) != 0) return (SMB_DOP_DECODE_ERROR); smbd_join(&jdi, &jdres); arg->rbuf = smb_common_encode(&jdres, smb_joinres_xdr, &arg->rsize); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } static int smbd_dop_get_dcinfo(smbd_arg_t *arg) { smb_domainex_t dxi; if (!smb_domain_getinfo(&dxi)) return (SMB_DOP_EMPTYBUF); arg->rbuf = smb_string_encode(dxi.d_dci.dc_name, &arg->rsize); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } /* * Return the number of snapshots for a dataset */ static int smbd_dop_vss_get_count(smbd_arg_t *arg) { smb_string_t path; uint32_t count; bzero(&path, sizeof (smb_string_t)); arg->rbuf = NULL; if (smb_string_decode(&path, arg->data, arg->datalen) != 0) return (SMB_DOP_DECODE_ERROR); if (smbd_vss_get_count(path.buf, &count) == 0) arg->rbuf = smb_common_encode(&count, xdr_uint32_t, &arg->rsize); xdr_free(smb_string_xdr, (char *)&path); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } /* * Return the count and list of snapshots. * The list is in @GMT token format. */ static int smbd_dop_vss_get_snapshots(smbd_arg_t *arg) { char **gmtp; smb_gmttoken_query_t request; smb_gmttoken_response_t reply; uint_t i; bzero(&request, sizeof (smb_gmttoken_query_t)); bzero(&reply, sizeof (smb_gmttoken_response_t)); if (smb_common_decode(arg->data, arg->datalen, smb_gmttoken_query_xdr, &request) != 0) return (SMB_DOP_DECODE_ERROR); reply.gtr_gmttokens.gtr_gmttokens_val = malloc(request.gtq_count * sizeof (char *)); bzero(reply.gtr_gmttokens.gtr_gmttokens_val, request.gtq_count * sizeof (char *)); if (reply.gtr_gmttokens.gtr_gmttokens_val == NULL) { xdr_free(smb_gmttoken_query_xdr, (char *)&request); return (SMB_DOP_EMPTYBUF); } smbd_vss_get_snapshots(request.gtq_path, request.gtq_count, &reply.gtr_count, &reply.gtr_gmttokens.gtr_gmttokens_len, reply.gtr_gmttokens.gtr_gmttokens_val); arg->rbuf = smb_common_encode(&reply, smb_gmttoken_response_xdr, &arg->rsize); if (arg->rbuf == NULL) { xdr_free(smb_gmttoken_query_xdr, (char *)&request); return (SMB_DOP_ENCODE_ERROR); } for (i = 0, gmtp = reply.gtr_gmttokens.gtr_gmttokens_val; (i < request.gtq_count); i++) { if (*gmtp) free(*gmtp); gmtp++; } free(reply.gtr_gmttokens.gtr_gmttokens_val); xdr_free(smb_gmttoken_query_xdr, (char *)&request); return (SMB_DOP_SUCCESS); } /* * Return the name of the snapshot that matches the dataset path * and @GMT token. */ static int smbd_dop_vss_map_gmttoken(smbd_arg_t *arg) { char *snapname; smb_gmttoken_snapname_t request; bzero(&request, sizeof (smb_gmttoken_snapname_t)); if (smb_common_decode(arg->data, arg->datalen, smb_gmttoken_snapname_xdr, &request) != 0) { xdr_free(smb_gmttoken_snapname_xdr, (char *)&request); return (SMB_DOP_DECODE_ERROR); } if ((snapname = malloc(MAXPATHLEN)) == NULL) { xdr_free(smb_gmttoken_snapname_xdr, (char *)&request); return (SMB_DOP_ENCODE_ERROR); } if ((smbd_vss_map_gmttoken(request.gts_path, request.gts_gmttoken, request.gts_toktime, snapname) != 0)) { *snapname = '\0'; } arg->rbuf = smb_string_encode(snapname, &arg->rsize); xdr_free(smb_gmttoken_snapname_xdr, (char *)&request); free(snapname); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } static int smbd_dop_ads_find_host(smbd_arg_t *arg) { smb_ads_host_info_t *hinfo = NULL; char *hostname = ""; smb_string_t fqdn; bzero(&fqdn, sizeof (smb_string_t)); if (smb_string_decode(&fqdn, arg->data, arg->datalen) != 0) return (SMB_DOP_DECODE_ERROR); if ((hinfo = smb_ads_find_host(fqdn.buf)) != NULL) hostname = hinfo->name; xdr_free(smb_string_xdr, (char *)&fqdn); arg->rbuf = smb_string_encode(hostname, &arg->rsize); free(hinfo); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } /* * Query the list of user/group quota entries for a given filesystem. */ static int smbd_dop_quota_query(smbd_arg_t *arg) { smb_quota_query_t request; smb_quota_response_t reply; uint32_t status; bzero(&request, sizeof (smb_quota_query_t)); bzero(&reply, sizeof (smb_quota_response_t)); if (smb_common_decode(arg->data, arg->datalen, smb_quota_query_xdr, &request) != 0) return (SMB_DOP_DECODE_ERROR); status = smb_quota_query(&request, &reply); reply.qr_status = status; arg->rbuf = smb_common_encode(&reply, smb_quota_response_xdr, &arg->rsize); xdr_free(smb_quota_query_xdr, (char *)&request); smb_quota_free(&reply); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } /* * Set a list of user/group quota entries for a given filesystem. */ static int smbd_dop_quota_set(smbd_arg_t *arg) { smb_quota_set_t request; uint32_t status = 0; bzero(&request, sizeof (smb_quota_set_t)); if (smb_common_decode(arg->data, arg->datalen, smb_quota_set_xdr, &request) != 0) return (SMB_DOP_DECODE_ERROR); status = smb_quota_set(&request); arg->rbuf = smb_common_encode(&status, xdr_uint32_t, &arg->rsize); xdr_free(smb_quota_set_xdr, (char *)&request); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } static int smbd_dop_dfs_get_referrals(smbd_arg_t *arg) { dfs_referral_query_t request; dfs_referral_response_t reply; bzero(&request, sizeof (request)); bzero(&reply, sizeof (reply)); if (smb_common_decode(arg->data, arg->datalen, dfs_referral_query_xdr, &request) != 0) return (SMB_DOP_DECODE_ERROR); reply.rp_status = dfs_get_referrals((const char *)request.rq_path, request.rq_type, &reply.rp_referrals); if (reply.rp_status != ERROR_SUCCESS) bzero(&reply.rp_referrals, sizeof (dfs_info_t)); arg->rbuf = smb_common_encode(&reply, dfs_referral_response_xdr, &arg->rsize); if (reply.rp_status == ERROR_SUCCESS) dfs_info_free(&reply.rp_referrals); xdr_free(dfs_referral_query_xdr, (char *)&request); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } static int smbd_dop_shr_hostaccess(smbd_arg_t *arg) { smb_shr_hostaccess_query_t request; uint32_t reply; bzero(&request, sizeof (request)); bzero(&reply, sizeof (reply)); if (smb_common_decode(arg->data, arg->datalen, smb_shr_hostaccess_query_xdr, &request) != 0) return (SMB_DOP_DECODE_ERROR); reply = smb_shr_hostaccess(&request.shq_ipaddr, request.shq_none, request.shq_ro, request.shq_rw, request.shq_flag); arg->rbuf = smb_common_encode(&reply, xdr_uint32_t, &arg->rsize); xdr_free(smb_shr_hostaccess_query_xdr, (char *)&request); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } static int smbd_dop_shr_exec(smbd_arg_t *arg) { smb_shr_execinfo_t request; int reply; bzero(&request, sizeof (request)); bzero(&reply, sizeof (reply)); if (smb_common_decode(arg->data, arg->datalen, smb_shr_execinfo_xdr, &request) != 0) return (SMB_DOP_DECODE_ERROR); reply = smb_shr_exec(&request); if (reply != 0) syslog(LOG_NOTICE, "Failed to execute %s command", (request.e_type == SMB_EXEC_MAP) ? "map" : "unmap"); arg->rbuf = smb_common_encode(&reply, xdr_int, &arg->rsize); xdr_free(smb_shr_execinfo_xdr, (char *)&request); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } /* ARGSUSED */ static int smbd_dop_notify_dc_changed(smbd_arg_t *arg) { smbd_dc_monitor_refresh(); return (SMB_DOP_SUCCESS); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright 2023-2024 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" #define SMBD_DC_MONITOR_ATTEMPTS 3 #define SMBD_DC_MONITOR_RETRY_INTERVAL 3 /* seconds */ #define SMBD_DC_MONITOR_INTERVAL 60 /* seconds */ extern smbd_t smbd; static mutex_t smbd_dc_mutex; static cond_t smbd_dc_cv; static void *smbd_dc_monitor(void *); static void smbd_dc_update(void); static int smbd_dc_check(smb_domainex_t *); /* Todo: static boolean_t smbd_set_netlogon_cred(void); */ static void smbd_join_workgroup(smb_joininfo_t *, smb_joinres_t *); static void smbd_join_domain(smb_joininfo_t *, smb_joinres_t *); /* * Launch the DC discovery and monitor thread. */ int smbd_dc_monitor_init(void) { pthread_attr_t attr; int rc; smb_ads_init(); if (smbd.s_secmode != SMB_SECMODE_DOMAIN) return (0); (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&smbd.s_dc_monitor_tid, &attr, smbd_dc_monitor, NULL); (void) pthread_attr_destroy(&attr); return (rc); } /* * Refresh the DC monitor. Called from SMF refresh and when idmap * finds a different DC from what we were using previously. * Update our domain (and current DC) information. */ void smbd_dc_monitor_refresh(void) { syslog(LOG_INFO, "smbd_dc_monitor_refresh"); smb_ddiscover_refresh(); (void) mutex_lock(&smbd_dc_mutex); smbd.s_dc_changed = B_TRUE; (void) cond_signal(&smbd_dc_cv); (void) mutex_unlock(&smbd_dc_mutex); } /*ARGSUSED*/ static void * smbd_dc_monitor(void *arg) { smb_domainex_t di; boolean_t ds_not_responding; boolean_t ds_cfg_changed; timestruc_t delay; int i; /* Wait for smb_dclocator_init() to complete. */ smbd_online_wait("smbd_dc_monitor"); smbd_dc_update(); while (smbd_online()) { ds_not_responding = B_FALSE; ds_cfg_changed = B_FALSE; delay.tv_sec = SMBD_DC_MONITOR_INTERVAL; delay.tv_nsec = 0; (void) mutex_lock(&smbd_dc_mutex); (void) cond_reltimedwait(&smbd_dc_cv, &smbd_dc_mutex, &delay); if (smbd.s_dc_changed) { smbd.s_dc_changed = B_FALSE; ds_cfg_changed = B_TRUE; /* NB: smb_ddiscover_refresh was called. */ } (void) mutex_unlock(&smbd_dc_mutex); if (ds_cfg_changed) { syslog(LOG_DEBUG, "smbd_dc_monitor: config changed"); goto rediscover; } if (!smb_domain_getinfo(&di)) { syslog(LOG_DEBUG, "smbd_dc_monitor: no domain info"); goto rediscover; } if (di.d_dci.dc_name[0] == '\0') { syslog(LOG_DEBUG, "smbd_dc_monitor: no DC name"); goto rediscover; } for (i = 0; i < SMBD_DC_MONITOR_ATTEMPTS; ++i) { if (smbd_dc_check(&di) == 0) { ds_not_responding = B_FALSE; break; } ds_not_responding = B_TRUE; (void) sleep(SMBD_DC_MONITOR_RETRY_INTERVAL); } if (ds_not_responding) { syslog(LOG_NOTICE, "smbd_dc_monitor: DC not responding: %s", di.d_dci.dc_name); smb_ddiscover_bad_dc(di.d_dci.dc_name); } if (ds_not_responding || ds_cfg_changed) { rediscover: /* * An smb_ads_refresh will be done by the * smb_ddiscover_service when necessary. * Note: smbd_dc_monitor_refresh was already * called if appropriate. */ smbd_dc_update(); } } smbd.s_dc_monitor_tid = 0; return (NULL); } /* * Simply attempt a connection to the DC. */ static int smbd_dc_check(smb_domainex_t *di) { struct sockaddr_storage sa; int salen = 0; int sock = -1; int tmo = 5 * 1000; /* 5 sec. */ int rc; bzero(&sa, sizeof (sa)); switch (di->d_dci.dc_addr.a_family) { case AF_INET: { struct sockaddr_in *sin = (void *)&sa; sin->sin_family = AF_INET; sin->sin_port = htons(IPPORT_SMB); sin->sin_addr.s_addr = di->d_dci.dc_addr.a_ipv4; salen = sizeof (*sin); break; } case AF_INET6: { struct sockaddr_in6 *sin6 = (void *)&sa; sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(IPPORT_SMB); (void) memcpy(&sin6->sin6_addr, &di->d_dci.dc_addr.a_ipv6, sizeof (in6_addr_t)); salen = sizeof (*sin6); break; } default: return (-1); } sock = socket(di->d_dci.dc_addr.a_family, SOCK_STREAM, 0); if (sock < 0) return (errno); (void) setsockopt(sock, IPPROTO_TCP, TCP_CONN_ABORT_THRESHOLD, &tmo, sizeof (tmo)); rc = connect(sock, (const struct sockaddr *)&sa, salen); if (rc < 0) rc = errno; (void) close(sock); return (rc); } /* * Locate a domain controller in the current resource domain and Update * the Netlogon credential chain. * * The domain configuration will be updated upon successful DC discovery. */ static void smbd_dc_update(void) { char domain[MAXHOSTNAMELEN]; smb_domainex_t info; smb_domain_t *di; DWORD status; /* * Don't want this active until we're a domain member. */ if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) return; if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) return; if (domain[0] == '\0') { syslog(LOG_NOTICE, "smbd_dc_update: no domain name set"); return; } if (!smb_locate_dc(domain, &info)) { syslog(LOG_NOTICE, "smbd_dc_update: %s: locate failed", domain); return; } di = &info.d_primary; syslog(LOG_INFO, "smbd_dc_update: %s: located %s", domain, info.d_dci.dc_name); status = mlsvc_netlogon(info.d_dci.dc_name, di->di_nbname); if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, "failed to establish NETLOGON credential chain"); syslog(LOG_NOTICE, " with server %s for domain %s (%s)", info.d_dci.dc_name, domain, xlate_nt_status(status)); } } /* * smbd_join * * Joins the specified domain/workgroup. * * If the security mode or domain name is being changed, * the caller must restart the service. */ void smbd_join(smb_joininfo_t *info, smb_joinres_t *res) { dssetup_clear_domain_info(); if (info->mode == SMB_SECMODE_WORKGRP) smbd_join_workgroup(info, res); else smbd_join_domain(info, res); } static void smbd_join_workgroup(smb_joininfo_t *info, smb_joinres_t *res) { char nb_domain[SMB_PI_MAX_DOMAIN]; syslog(LOG_DEBUG, "smbd: join workgroup: %s", info->domain_name); (void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain, sizeof (nb_domain)); (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE); smbd_set_secmode(SMB_SECMODE_WORKGRP); smb_config_setdomaininfo(info->domain_name, "", "", "", ""); (void) smb_config_set_idmap_domain(""); (void) smb_config_refresh_idmap(); if (strcasecmp(nb_domain, info->domain_name)) smb_browser_reconfig(); res->status = NT_STATUS_SUCCESS; } static void smbd_join_domain(smb_joininfo_t *info, smb_joinres_t *res) { syslog(LOG_DEBUG, "smbd: join domain: %s", info->domain_name); /* info->domain_name could either be NetBIOS domain name or FQDN */ mlsvc_join(info, res); if (res->status == 0) { smbd_set_secmode(SMB_SECMODE_DOMAIN); } else { syslog(LOG_ERR, "smbd: failed joining %s (%s)", info->domain_name, xlate_nt_status(res->status)); } } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2020 Nexenta by DDN, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" #include "locate_plugin.h" /* osconf.h - sigh */ #define KRB5_DEFAULT_PORT 88 #define DEFAULT_KADM5_PORT 749 #define DEFAULT_KPASSWD_PORT 464 /* * This is an "override plugin" used by libkrb5. See: * lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c * * The interface is based on: * http://web.mit.edu/~kerberos/krb5-1.12/doc/plugindev/locate.html */ /* * Called by krb5int_locate_server / override_locate_server */ krb5_error_code _krb5_override_service_locator( void *arg0, enum locate_service_type svc, const char *realm, int socktype, int family, int (*cbfunc)(void *, int, struct sockaddr *), void *cbdata) { _NOTE(ARGUNUSED(arg0)) smb_domainex_t dxi; int rc = KRB5_PLUGIN_NO_HANDLE; short port; /* * Is this a service we want to override? */ switch (svc) { case locate_service_kdc: case locate_service_master_kdc: port = htons(KRB5_DEFAULT_PORT); break; case locate_service_kadmin: port = htons(DEFAULT_KADM5_PORT); break; case locate_service_kpasswd: port = htons(DEFAULT_KPASSWD_PORT); break; case locate_service_krb524: default: return (rc); } /* * What's my domain? Note: have to get this in a way * that works while join domain is underway. */ if (!smb_domain_getinfo(&dxi)) { smbd_report("_krb5_override_service_locator " "failed getting domain info"); return (KRB5_ERR_HOST_REALM_UNKNOWN); } /* * Is this a realm we want to override? */ if (0 != strcasecmp(realm, dxi.d_primary.di_fqname)) { syslog(LOG_DEBUG, "_krb5_override_service_locator, " "realm=%s, fqdn=%s", realm, dxi.d_primary.di_fqname); return (rc); } /* * Yes, this is our domain. Have a DC? */ if (dxi.d_dci.dc_name[0] == '\0' || dxi.d_dci.dc_addr.a_family == 0) return (KRB5_REALM_CANT_RESOLVE); if ((dxi.d_dci.dc_flags & DS_KDC_FLAG) == 0) { smbd_report("_krb5_override_service_locator: " "Domain Controller is not a KDC: " "Kerberos auth may be slow"); return (rc); } switch (family) { case AF_UNSPEC: break; /* OK */ case AF_INET: case AF_INET6: if (family == dxi.d_dci.dc_addr.a_family) break; /* OK */ /* else fallthrough */ default: return (KRB5_ERR_NO_SERVICE); } /* * Provide the service address we have. */ switch (dxi.d_dci.dc_addr.a_family) { case AF_INET: { struct sockaddr_in sin; (void) memset(&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_port = port; (void) memcpy(&sin.sin_addr, &dxi.d_dci.dc_addr.a_ipv4, sizeof (sin.sin_addr)); rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin); /* rc from cbfunc is special. */ if (rc) rc = ENOMEM; break; } case AF_INET6: { struct sockaddr_in6 sin6; (void) memset(&sin6, 0, sizeof (sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = port; (void) memcpy(&sin6.sin6_addr, &dxi.d_dci.dc_addr.a_ipv6, sizeof (sin6.sin6_addr)); rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin6); /* rc from cbfunc is special. */ if (rc) rc = ENOMEM; break; } default: rc = KRB5_ERR_NO_SERVICE; break; } return (rc); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2022 Tintri by DDN, Inc. All rights reserved. */ /* * SPNEGO back-end for Kerberos. See [MS-KILE] */ #include #include #include #include #include "smbd.h" #include "smbd_authsvc.h" /* From krb5/krb/pac.c (should have been exported) */ #define PAC_LOGON_INFO 1 typedef struct krb5ssp_backend { gss_ctx_id_t be_gssctx; char *be_username; gss_buffer_desc be_authz_pac; krb5_context be_kctx; krb5_pac be_kpac; krb5_data be_pac; } krb5ssp_backend_t; static uint32_t get_authz_data_pac( gss_ctx_id_t context_handle, gss_buffer_t ad_data); static uint32_t get_ssnkey(authsvc_context_t *ctx); /* * Initialize this context for Kerberos, if possible. * * Should not get here unless libsmb smb_config_get_negtok * includes the Kerberos5 Mech OIDs in our spnego hint. * * Todo: allocate ctx->ctx_backend * See: krb5_gss_accept_sec_context() */ int smbd_krb5ssp_init(authsvc_context_t *ctx) { krb5ssp_backend_t *be; be = malloc(sizeof (*be)); if (be == 0) return (NT_STATUS_NO_MEMORY); bzero(be, sizeof (*be)); be->be_gssctx = GSS_C_NO_CONTEXT; ctx->ctx_backend = be; return (0); } /* * Todo: free ctx->ctx_backend */ void smbd_krb5ssp_fini(authsvc_context_t *ctx) { krb5ssp_backend_t *be = ctx->ctx_backend; uint32_t minor; if (be == NULL) return; if (be->be_kctx != NULL) { krb5_free_data_contents(be->be_kctx, &be->be_pac); if (be->be_kpac != NULL) krb5_pac_free(be->be_kctx, be->be_kpac); krb5_free_context(be->be_kctx); } (void) gss_release_buffer(NULL, &be->be_authz_pac); free(be->be_username); if (be->be_gssctx != GSS_C_NO_CONTEXT) { (void) gss_delete_sec_context(&minor, &be->be_gssctx, GSS_C_NO_BUFFER); } free(be); } static char *krb5ssp_def_username = "Unknown-Kerberos-User"; static char *krb5ssp_def_domain = "Unknown-Domain"; /* * Handle a Kerberos auth message. * * State across messages is in ctx->ctx_backend. * * Equivalent to smbd_user_auth_logon(). */ int smbd_krb5ssp_work(authsvc_context_t *ctx) { gss_buffer_desc intok, outtok; gss_buffer_desc namebuf; krb5ssp_backend_t *be = ctx->ctx_backend; gss_name_t gname = NULL; OM_uint32 major, minor, ret_flags; gss_OID name_type = GSS_C_NULL_OID; gss_OID mech_type = GSS_C_NULL_OID; krb5_error_code kerr; uint32_t status; smb_token_t *token = NULL; intok.length = ctx->ctx_ibodylen; intok.value = ctx->ctx_ibodybuf; bzero(&outtok, sizeof (gss_buffer_desc)); bzero(&namebuf, sizeof (gss_buffer_desc)); assert(be->be_username == NULL); /* Do this early, for error message support. */ kerr = krb5_init_context(&be->be_kctx); if (kerr != 0) { smbd_report("krb5ssp, krb5_init_ctx: %s", krb5_get_error_message(be->be_kctx, kerr)); return (NT_STATUS_INTERNAL_ERROR); } major = gss_accept_sec_context(&minor, &be->be_gssctx, GSS_C_NO_CREDENTIAL, &intok, GSS_C_NO_CHANNEL_BINDINGS, &gname, &mech_type, &outtok, &ret_flags, NULL, NULL); if (outtok.length == 0) ctx->ctx_obodylen = 0; else if (outtok.length <= ctx->ctx_obodylen) { ctx->ctx_obodylen = outtok.length; (void) memcpy(ctx->ctx_obodybuf, outtok.value, outtok.length); free(outtok.value); outtok.value = NULL; } else { free(ctx->ctx_obodybuf); ctx->ctx_obodybuf = outtok.value; ctx->ctx_obodylen = outtok.length; outtok.value = NULL; } if (GSS_ERROR(major)) { smbd_report("krb5ssp: gss_accept_sec_context, " "mech=0x%x, major=0x%x, minor=0x%x", (int)mech_type, major, minor); smbd_report(" krb5: %s", krb5_get_error_message(be->be_kctx, minor)); status = NT_STATUS_WRONG_PASSWORD; goto out; } switch (major) { case GSS_S_COMPLETE: break; case GSS_S_CONTINUE_NEEDED: if (outtok.length > 0) { ctx->ctx_orawtype = LSA_MTYPE_ES_CONT; /* becomes NT_STATUS_MORE_PROCESSING_REQUIRED */ return (0); } status = NT_STATUS_WRONG_PASSWORD; goto out; default: status = NT_STATUS_WRONG_PASSWORD; goto out; } /* * OK, we got GSS_S_COMPLETE. Get the name so we can use it * in log messages if we get failures decoding the PAC etc. * Then get the PAC, decode it, build the logon token. */ if (gname != NULL && GSS_S_COMPLETE == gss_display_name(&minor, gname, &namebuf, &name_type)) { /* Save the user name. */ be->be_username = strdup(namebuf.value); (void) gss_release_buffer(&minor, &namebuf); (void) gss_release_name(&minor, &gname); if (be->be_username == NULL) { return (NT_STATUS_NO_MEMORY); } } /* * Extract the KRB5_AUTHDATA_WIN2K_PAC data. */ status = get_authz_data_pac(be->be_gssctx, &be->be_authz_pac); if (status) goto out; kerr = krb5_pac_parse(be->be_kctx, be->be_authz_pac.value, be->be_authz_pac.length, &be->be_kpac); if (kerr) { smbd_report("krb5ssp, krb5_pac_parse: %s", krb5_get_error_message(be->be_kctx, kerr)); status = NT_STATUS_UNSUCCESSFUL; goto out; } kerr = krb5_pac_get_buffer(be->be_kctx, be->be_kpac, PAC_LOGON_INFO, &be->be_pac); if (kerr) { smbd_report("krb5ssp, krb5_pac_get_buffer: %s", krb5_get_error_message(be->be_kctx, kerr)); status = NT_STATUS_UNSUCCESSFUL; goto out; } ctx->ctx_token = calloc(1, sizeof (smb_token_t)); if (ctx->ctx_token == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } status = smb_decode_krb5_pac(ctx->ctx_token, be->be_pac.data, be->be_pac.length); if (status) goto out; status = get_ssnkey(ctx); if (status) goto out; if (!smb_token_setup_common(ctx->ctx_token)) { status = NT_STATUS_UNSUCCESSFUL; goto out; } /* Success! */ ctx->ctx_orawtype = LSA_MTYPE_ES_DONE; status = 0; token = ctx->ctx_token; /* * Before we return, audit successful and failed logons. * * Successful logons are audited using the username and domain * contained in the ticket (where the domain comes from the PAC data). * * Failed logins use a 'default' domain. If we fail after obtaining * the username in the ticket, we audit under that username. * * Prior to decoding the username, we only audit failures where we'll * return NT_STATUS_WRONG_PASSWORD, so that we audit attempts with * invalid (or forged) tickets. These records use a 'default' username; * As such, they serve only to inform an administrator that * a particular client used a bad ticket, but does not contain any * information on the ticket itself. * * Once we have a username, we'll audit all failed authentications. */ out: status = smbd_logon_final(token, &ctx->ctx_clinfo.lci_clnt_ipaddr, (be->be_username != NULL) ? be->be_username : krb5ssp_def_username, krb5ssp_def_domain, status); return (status); } /* * See: GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID * and: KRB5_AUTHDATA_WIN2K_PAC */ static const gss_OID_desc oid_ex_authz_data_pac = { 13, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a\x81\x00" }; /* * See: krb5_gss_inquire_sec_context_by_oid() * and krb5_gss_inquire_sec_context_by_oid_ops[], * gss_krb5int_extract_authz_data_from_sec_context() */ static uint32_t get_authz_data_pac( gss_ctx_id_t context_handle, gss_buffer_t ad_data) { gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; OM_uint32 major, minor; uint32_t status = NT_STATUS_UNSUCCESSFUL; if (ad_data == NULL) goto out; major = gss_inquire_sec_context_by_oid( &minor, context_handle, (gss_OID)&oid_ex_authz_data_pac, &data_set); if (GSS_ERROR(major)) { smbd_report("krb5ssp, gss_inquire...PAC, " "major=0x%x, minor=0x%x", major, minor); goto out; } if ((data_set == GSS_C_NO_BUFFER_SET) || (data_set->count == 0)) { goto out; } /* Only need the first element? */ ad_data->length = data_set->elements[0].length; ad_data->value = malloc(ad_data->length); if (ad_data->value == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } bcopy(data_set->elements[0].value, ad_data->value, ad_data->length); status = 0; out: (void) gss_release_buffer_set(&minor, &data_set); return (status); } /* * Get the session key, and save it in the token. * * See: krb5_gss_inquire_sec_context_by_oid(), * krb5_gss_inquire_sec_context_by_oid_ops[], and * gss_krb5int_inq_session_key */ static uint32_t get_ssnkey(authsvc_context_t *ctx) { krb5ssp_backend_t *be = ctx->ctx_backend; gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; OM_uint32 major, minor; size_t keylen; uint32_t status = NT_STATUS_UNSUCCESSFUL; major = gss_inquire_sec_context_by_oid(&minor, be->be_gssctx, GSS_C_INQ_SSPI_SESSION_KEY, &data_set); if (GSS_ERROR(major)) { smbd_report("krb5ssp, failed to get session key, " "major=0x%x, minor=0x%x", major, minor); goto out; } /* * The key is in the first element */ if (data_set == GSS_C_NO_BUFFER_SET || data_set->count == 0 || data_set->elements[0].length == 0 || data_set->elements[0].value == NULL) { smbd_report("krb5ssp: Session key is missing"); goto out; } if ((keylen = data_set->elements[0].length) < SMBAUTH_HASH_SZ) { smbd_report("krb5ssp: Session key too short (%d)", data_set->elements[0].length); goto out; } ctx->ctx_token->tkn_ssnkey.val = malloc(keylen); if (ctx->ctx_token->tkn_ssnkey.val == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } ctx->ctx_token->tkn_ssnkey.len = keylen; bcopy(data_set->elements[0].value, ctx->ctx_token->tkn_ssnkey.val, keylen); status = 0; out: (void) gss_release_buffer_set(&minor, &data_set); return (status); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2022 Tintri by DDN, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" /* * An audit session is established at user logon and terminated at user * logoff. * * SMB audit handles are allocated when users logon (SmbSessionSetupX) * and deallocted when a user logs off (SmbLogoffX). Each time an SMB * audit handle is allocated it is added to a global list. */ typedef struct smb_audit { struct smb_audit *sa_next; adt_session_data_t *sa_handle; uid_t sa_uid; gid_t sa_gid; uint32_t sa_audit_sid; uint32_t sa_refcnt; char *sa_domain; char *sa_username; } smb_audit_t; static smb_audit_t *smbd_audit_list; static mutex_t smbd_audit_lock; /* * Unique identifier for audit sessions in the audit list. * Used to lookup an audit session on logoff. */ static uint32_t smbd_audit_sid; static void smbd_audit_link(smb_audit_t *); static smb_audit_t *smbd_audit_unlink(uint32_t); /* * Invoked at user logon due to SmbSessionSetupX. Authenticate the * user. * * On error, returns NULL, and status in user_info->lg_status. * * Equivalent to smbd_krb5ssp_work(). */ smb_token_t * smbd_user_auth_logon(smb_logon_t *user_info) { smb_token_t *token = NULL; smb_logon_t tmp_user; char *p; char *buf = NULL; if (user_info->lg_username == NULL || user_info->lg_domain == NULL || user_info->lg_workstation == NULL) { user_info->lg_status = NT_STATUS_INVALID_PARAMETER; return (NULL); } /* * Avoid modifying the caller-provided struct because it * may or may not point to allocated strings etc. * Copy to tmp_user, auth, then copy the (out) lg_status * member back to the caller-provided struct. */ tmp_user = *user_info; if (tmp_user.lg_username[0] == '\0') { tmp_user.lg_flags |= SMB_ATF_ANON; tmp_user.lg_e_username = "anonymous"; } else { tmp_user.lg_e_username = tmp_user.lg_username; } /* Handle user@domain format. */ if (tmp_user.lg_domain[0] == '\0' && (p = strchr(tmp_user.lg_e_username, '@')) != NULL) { buf = strdup(tmp_user.lg_e_username); if (buf == NULL) { user_info->lg_status = NT_STATUS_NO_MEMORY; return (NULL); } p = buf + (p - tmp_user.lg_e_username); *p = '\0'; tmp_user.lg_e_domain = p + 1; tmp_user.lg_e_username = buf; } else { tmp_user.lg_e_domain = tmp_user.lg_domain; } token = smb_logon(&tmp_user); if (token == NULL && tmp_user.lg_status == 0) /* should not happen */ user_info->lg_status = NT_STATUS_INTERNAL_ERROR; else user_info->lg_status = tmp_user.lg_status; user_info->lg_status = smbd_logon_final(token, &user_info->lg_clnt_ipaddr, tmp_user.lg_e_username, tmp_user.lg_e_domain, user_info->lg_status); free(buf); if (user_info->lg_status != 0) { smb_token_destroy(token); token = NULL; } return (token); } /* Start an audit session and audit the event. */ static boolean_t smbd_logon_audit(smb_token_t *token, smb_inaddr_t *ipaddr, char *username, char *domain) { smb_audit_t *entry; adt_session_data_t *ah = NULL; adt_event_data_t *event; au_tid_addr_t termid; char sidbuf[SMB_SID_STRSZ]; uid_t uid; gid_t gid; char *sid; int status; int retval; assert(username != NULL); assert(domain != NULL); if (token == NULL) { uid = ADT_NO_ATTRIB; gid = ADT_NO_ATTRIB; sid = NT_NULL_SIDSTR; /* use the 'default' username and domain we were given */ status = ADT_FAILURE; retval = ADT_FAIL_VALUE_AUTH; } else { uid = token->tkn_user.i_id; gid = token->tkn_primary_grp.i_id; smb_sid_tostr(token->tkn_user.i_sid, sidbuf); sid = sidbuf; username = token->tkn_account_name; domain = token->tkn_domain_name; status = ADT_SUCCESS; retval = ADT_SUCCESS; } if (adt_start_session(&ah, NULL, 0)) { syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m"); goto errout; } if ((event = adt_alloc_event(ah, ADT_smbd_session)) == NULL) { syslog(LOG_AUTH | LOG_ALERT, "adt_alloc_event(ADT_smbd_session): %m"); goto errout; } (void) memset(&termid, 0, sizeof (au_tid_addr_t)); termid.at_port = IPPORT_SMB; if (ipaddr->a_family == AF_INET) { termid.at_addr[0] = ipaddr->a_ipv4; termid.at_type = AU_IPv4; } else { bcopy(&ipaddr->a_ip, termid.at_addr, sizeof (in6_addr_t)); termid.at_type = AU_IPv6; } adt_set_termid(ah, &termid); if (adt_set_user(ah, uid, gid, uid, gid, NULL, ADT_NEW)) { syslog(LOG_AUTH | LOG_ALERT, "adt_set_user: %m"); adt_free_event(event); goto errout; } event->adt_smbd_session.domain = domain; event->adt_smbd_session.username = username; event->adt_smbd_session.sid = sid; if (adt_put_event(event, status, retval)) syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m"); adt_free_event(event); if (token) { if ((entry = malloc(sizeof (smb_audit_t))) == NULL) { syslog(LOG_ERR, "smbd_user_auth_logon: %m"); goto errout; } entry->sa_handle = ah; entry->sa_uid = uid; entry->sa_gid = gid; entry->sa_username = strdup(username); entry->sa_domain = strdup(domain); smbd_audit_link(entry); token->tkn_audit_sid = entry->sa_audit_sid; } return (B_TRUE); errout: (void) adt_end_session(ah); return (B_FALSE); } /* * Handles all of the work needed to be done after SMB authentication, * regardless of the auth flavor (Kerberos or NTLM). * * This should return the original status to the caller, unless something * here causes us to turn what would be a success into a failure * (or we decide we should override the original error for some reason). */ uint32_t smbd_logon_final(smb_token_t *token, smb_inaddr_t *ipaddr, char *username, char *domain, uint32_t status) { assert(token != NULL || status != 0); if (!smbd_logon_audit(token, ipaddr, username, domain) && status == 0) return (NT_STATUS_AUDIT_FAILED); if (status == 0) smb_autohome_add(token); return (status); } /* * Logon due to a subsequent SmbSessionSetupX on an existing session. * The user was authenticated during the initial session setup. */ void smbd_user_nonauth_logon(uint32_t audit_sid) { smb_audit_t *entry; (void) mutex_lock(&smbd_audit_lock); entry = smbd_audit_list; while (entry) { if (entry->sa_audit_sid == audit_sid) { ++entry->sa_refcnt; break; } entry = entry->sa_next; } (void) mutex_unlock(&smbd_audit_lock); } /* * Invoked at user logoff due to SMB Logoff. If this is the final * logoff for this user on the session, audit the event and terminate * the audit session. * * This is called to logoff both NTLMSSP and KRB5SSP authentications. */ void smbd_user_auth_logoff(uint32_t audit_sid) { smb_audit_t *entry; adt_session_data_t *ah; adt_event_data_t *event; struct passwd pw; char buf[NSS_LINELEN_PASSWD]; if ((entry = smbd_audit_unlink(audit_sid)) == NULL) return; if (IDMAP_ID_IS_EPHEMERAL(entry->sa_uid)) { smb_autohome_remove(entry->sa_username); } else { if (getpwuid_r(entry->sa_uid, &pw, buf, sizeof (buf)) == NULL) return; smb_autohome_remove(pw.pw_name); } ah = entry->sa_handle; if ((event = adt_alloc_event(ah, ADT_smbd_logoff)) == NULL) { syslog(LOG_AUTH | LOG_ALERT, "adt_alloc_event(ADT_smbd_logoff): %m"); } else { event->adt_smbd_logoff.domain = entry->sa_domain; event->adt_smbd_logoff.username = entry->sa_username; if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS)) syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m"); adt_free_event(event); } (void) adt_end_session(ah); free(entry->sa_username); free(entry->sa_domain); free(entry); } /* * Allocate an id and link an audit handle onto the global list. */ static void smbd_audit_link(smb_audit_t *entry) { (void) mutex_lock(&smbd_audit_lock); do { ++smbd_audit_sid; } while ((smbd_audit_sid == 0) || (smbd_audit_sid == (uint32_t)-1)); entry->sa_audit_sid = smbd_audit_sid; entry->sa_refcnt = 1; entry->sa_next = smbd_audit_list; smbd_audit_list = entry; (void) mutex_unlock(&smbd_audit_lock); } /* * Unlink an audit handle. If the reference count reaches 0, the entry * is removed from the list and returned. Otherwise the entry remains * on the list and a null pointer is returned. */ static smb_audit_t * smbd_audit_unlink(uint32_t audit_sid) { smb_audit_t *entry; smb_audit_t **ppe; (void) mutex_lock(&smbd_audit_lock); ppe = &smbd_audit_list; while (*ppe) { entry = *ppe; if (entry->sa_audit_sid == audit_sid) { if (entry->sa_refcnt == 0) break; if ((--entry->sa_refcnt) != 0) break; *ppe = entry->sa_next; (void) mutex_unlock(&smbd_audit_lock); return (entry); } ppe = &(*ppe)->sa_next; } (void) mutex_unlock(&smbd_audit_lock); return (NULL); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2018 Nexenta Systems, Inc. All rights reserved. * Copyright 2022 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" #define SECSPERMIN 60 #define SMBD_ONLINE_WAIT_INTERVAL 10 #define SMBD_REFRESH_INTERVAL 10 #define SMB_DBDIR "/var/smb" static int smbd_daemonize_init(void); static void smbd_daemonize_fini(int, int); static int smb_init_daemon_priv(int, uid_t, gid_t); static int smbd_kernel_bind(void); static void smbd_kernel_unbind(void); static int smbd_already_running(void); static int smbd_service_init(void); static void smbd_service_fini(void); static int smbd_setup_options(int argc, char *argv[]); static void smbd_usage(FILE *fp); static int32_t smbd_gmtoff(void); static void smbd_localtime_init(void); static void *smbd_localtime_monitor(void *arg); static void smbd_dyndns_init(void); static void smbd_load_shares(void); static void *smbd_share_loader(void *); static void smbd_refresh_handler(void); static int smbd_kernel_start(void); smbd_t smbd; /* * Use SMF error codes only on return or exit. */ int main(int argc, char *argv[]) { sigset_t set; uid_t uid; int pfd = -1; int sigval; struct rlimit rl; int orig_limit; #ifdef FKSMBD fksmbd_init(); #endif smbd.s_pname = basename(argv[0]); openlog(smbd.s_pname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); if (smbd_setup_options(argc, argv) != 0) return (SMF_EXIT_ERR_FATAL); if ((uid = getuid()) != smbd.s_uid) { #ifdef FKSMBD /* Can't manipulate privileges in daemonize. */ if (smbd.s_fg == 0) { smbd.s_fg = 1; smbd_report("user %d (forced -f)", uid); } #else /* FKSMBD */ smbd_report("user %d: %s", uid, strerror(EPERM)); return (SMF_EXIT_ERR_FATAL); #endif /* FKSMBD */ } if (is_system_labeled()) { smbd_report("Trusted Extensions not supported"); return (SMF_EXIT_ERR_FATAL); } if (smbd_already_running()) return (SMF_EXIT_OK); /* * Raise the file descriptor limit to accommodate simultaneous user * authentications/file access. */ if ((getrlimit(RLIMIT_NOFILE, &rl) == 0) && (rl.rlim_cur < rl.rlim_max)) { orig_limit = rl.rlim_cur; rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_NOFILE, &rl) != 0) smbd_report("Failed to raise file descriptor limit" " from %d to %d", orig_limit, rl.rlim_cur); } /* * Block async signals in all threads. */ (void) sigemptyset(&set); (void) sigaddset(&set, SIGHUP); (void) sigaddset(&set, SIGINT); (void) sigaddset(&set, SIGQUIT); (void) sigaddset(&set, SIGPIPE); (void) sigaddset(&set, SIGTERM); (void) sigaddset(&set, SIGUSR1); (void) sigaddset(&set, SIGUSR2); (void) sigprocmask(SIG_SETMASK, &set, NULL); if (smbd.s_fg) { if (smbd_service_init() != 0) { smbd_report("service initialization failed"); exit(SMF_EXIT_ERR_FATAL); } } else { /* * "pfd" is a pipe descriptor -- any fatal errors * during subsequent initialization of the child * process should be written to this pipe and the * parent will report this error as the exit status. */ pfd = smbd_daemonize_init(); if (smbd_service_init() != 0) { smbd_report("daemon initialization failed"); exit(SMF_EXIT_ERR_FATAL); } smbd_daemonize_fini(pfd, SMF_EXIT_OK); } while (!smbd.s_shutting_down) { sigval = sigwait(&set); switch (sigval) { case -1: syslog(LOG_DEBUG, "sigwait failed: %s", strerror(errno)); break; case SIGPIPE: break; case SIGHUP: syslog(LOG_DEBUG, "refresh requested"); smbd_refresh_handler(); break; case SIGUSR1: syslog(LOG_DEBUG, "SIGUSR1 ignored"); break; default: /* * Typically SIGINT or SIGTERM. */ smbd.s_shutting_down = B_TRUE; break; } } /* * Allow termination signals while shutting down. */ (void) sigemptyset(&set); if (smbd.s_fg) { (void) sigaddset(&set, SIGHUP); (void) sigaddset(&set, SIGINT); } (void) sigaddset(&set, SIGTERM); (void) sigprocmask(SIG_UNBLOCK, &set, NULL); smbd_service_fini(); return ((smbd.s_fatal_error) ? SMF_EXIT_ERR_FATAL : SMF_EXIT_OK); } /* * This function will fork off a child process, * from which only the child will return. * * Use SMF error codes only on exit. */ static int smbd_daemonize_init(void) { int status, pfds[2]; sigset_t set, oset; pid_t pid; int rc; /* * Reset privileges to the minimum set required. We continue * to run as root to create and access files in /var. */ rc = smb_init_daemon_priv(PU_RESETGROUPS, smbd.s_uid, smbd.s_gid); if (rc != 0) { smbd_report("insufficient privileges"); exit(SMF_EXIT_ERR_FATAL); } /* * Block all signals prior to the fork and leave them blocked in the * parent so we don't get in a situation where the parent gets SIGINT * and returns non-zero exit status and the child is actually running. * In the child, restore the signal mask once we've done our setsid(). */ (void) sigfillset(&set); (void) sigdelset(&set, SIGABRT); (void) sigprocmask(SIG_BLOCK, &set, &oset); if (pipe(pfds) == -1) { smbd_report("unable to create pipe"); exit(SMF_EXIT_ERR_FATAL); } closelog(); if ((pid = fork()) == -1) { openlog(smbd.s_pname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); smbd_report("unable to fork"); closelog(); exit(SMF_EXIT_ERR_FATAL); } /* * If we're the parent process, wait for either the child to send us * the appropriate exit status over the pipe or for the read to fail * (presumably with 0 for EOF if our child terminated abnormally). * If the read fails, exit with either the child's exit status if it * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal. */ if (pid != 0) { (void) close(pfds[1]); if (read(pfds[0], &status, sizeof (status)) == sizeof (status)) _exit(status); if (waitpid(pid, &status, 0) == pid && WIFEXITED(status)) _exit(WEXITSTATUS(status)); _exit(SMF_EXIT_ERR_FATAL); } openlog(smbd.s_pname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); (void) setsid(); (void) sigprocmask(SIG_SETMASK, &oset, NULL); (void) chdir("/"); (void) umask(022); (void) close(pfds[0]); return (pfds[1]); } /* * This function is based on __init_daemon_priv() and replaces * __init_daemon_priv() since we want smbd to have all privileges so that it * can execute map/unmap commands with all privileges during share * connection/disconnection. Unused privileges are disabled until command * execution. The permitted and the limit set contains all privileges. The * inheritable set contains no privileges. */ static const char root_cp[] = "/core.%f.%t"; static const char daemon_cp[] = "/var/tmp/core.%f.%t"; static int smb_init_daemon_priv(int flags, uid_t uid, gid_t gid) { priv_set_t *perm = NULL; int ret = -1; char buf[1024]; /* * This is not a significant failure: it allows us to start programs * with sufficient privileges and with the proper uid. We don't * care enough about the extra groups in that case. */ if (flags & PU_RESETGROUPS) (void) setgroups(0, NULL); if (gid != (gid_t)-1 && setgid(gid) != 0) goto end; perm = priv_allocset(); if (perm == NULL) goto end; /* E = P */ (void) getppriv(PRIV_PERMITTED, perm); (void) setppriv(PRIV_SET, PRIV_EFFECTIVE, perm); /* Now reset suid and euid */ if (uid != (uid_t)-1 && setreuid(uid, uid) != 0) goto end; /* I = 0 */ priv_emptyset(perm); ret = setppriv(PRIV_SET, PRIV_INHERITABLE, perm); end: priv_freeset(perm); if (core_get_process_path(buf, sizeof (buf), getpid()) == 0 && strcmp(buf, "core") == 0) { if ((uid == (uid_t)-1 ? geteuid() : uid) == 0) { (void) core_set_process_path(root_cp, sizeof (root_cp), getpid()); } else { (void) core_set_process_path(daemon_cp, sizeof (daemon_cp), getpid()); } } (void) setpflags(__PROC_PROTECT, 0); return (ret); } /* * Most privileges, except the ones that are required for smbd, are turn off * in the effective set. They will be turn on when needed for command * execution during share connection/disconnection. */ static void smbd_daemonize_fini(int fd, int exit_status) { priv_set_t *pset; /* * Now that we're running, if a pipe fd was specified, write an exit * status to it to indicate that our parent process can safely detach. * Then proceed to loading the remaining non-built-in modules. */ if (fd >= 0) (void) write(fd, &exit_status, sizeof (exit_status)); (void) close(fd); pset = priv_allocset(); if (pset == NULL) return; priv_basicset(pset); /* list of privileges for smbd */ (void) priv_addset(pset, PRIV_NET_MAC_AWARE); (void) priv_addset(pset, PRIV_NET_PRIVADDR); (void) priv_addset(pset, PRIV_PROC_AUDIT); (void) priv_addset(pset, PRIV_SYS_CONFIG); (void) priv_addset(pset, PRIV_SYS_DEVICES); (void) priv_addset(pset, PRIV_SYS_SMB); (void) priv_addset(pset, PRIV_SYS_MOUNT); priv_inverse(pset); /* turn off unneeded privileges */ (void) setppriv(PRIV_OFF, PRIV_EFFECTIVE, pset); priv_freeset(pset); /* reenable core dumps */ __fini_daemon_priv(NULL); } /* * smbd_service_init */ static int smbd_service_init(void) { static struct dir { char *name; int perm; } dir[] = { { SMB_DBDIR, 0700 }, { SMB_CVOL, 0755 }, { SMB_SYSROOT, 0755 }, { SMB_SYSTEM32, 0755 }, { SMB_VSS, 0755 }, { SMB_PIPE_DIR, 0755 }, { "/var/smb/lipc", 0755 }, }; int rc, i; smbd.s_pid = getpid(); /* * Stop for a debugger attach here, which is after the * fork() etc. in smb_daemonize_init() */ if (smbd.s_dbg_stop) { smbd_report("pid %d stop for debugger attach", smbd.s_pid); (void) kill(smbd.s_pid, SIGSTOP); } smbd_report("smbd starting, pid %d", smbd.s_pid); for (i = 0; i < sizeof (dir)/sizeof (dir[0]); ++i) { if ((mkdir(dir[i].name, dir[i].perm) < 0) && (errno != EEXIST)) { smbd_report("mkdir %s: %s", dir[i].name, strerror(errno)); return (-1); } } /* * This environment variable tells mech_krb5 to give us * MS-compatible behavior. */ (void) putenv("MS_INTEROP=1"); if ((rc = smb_ccache_init(SMB_VARRUN_DIR, SMB_CCACHE_FILE)) != 0) { if (rc == -1) smbd_report("mkdir %s: %s", SMB_VARRUN_DIR, strerror(errno)); else smbd_report("unable to set KRB5CCNAME"); return (-1); } #ifndef FKSMBD /* Upgrade SMF settings, if necessary. */ smb_config_upgrade(); #endif smb_codepage_init(); rc = smbd_cups_init(); if (smb_config_getbool(SMB_CI_PRINT_ENABLE)) smbd_report("print service %savailable", (rc == 0) ? "" : "un"); if (smbd_nicmon_start(SMBD_DEFAULT_INSTANCE_FMRI) != 0) smbd_report("NIC monitor failed to start"); smbd_dyndns_init(); smb_ipc_init(); if (smb_config_getbool(SMB_CI_NETBIOS_ENABLE) == 0) smbd_report("NetBIOS services disabled"); else if (smb_netbios_start() != 0) smbd_report("NetBIOS services failed to start"); else smbd_report("NetBIOS services started"); smbd.s_secmode = smb_config_get_secmode(); if ((rc = smb_domain_init(smbd.s_secmode)) != 0) { if (rc == SMB_DOMAIN_NOMACHINE_SID) { smbd_report( "no machine SID: check idmap configuration"); return (-1); } } if (smbd_dc_monitor_init() != 0) smbd_report("DC monitor initialization failed %s", strerror(errno)); if (smbd_pipesvc_start() != 0) { smbd_report("pipesvc initialization failed"); return (-1); } if (smbd_authsvc_start() != 0) { smbd_report("authsvc initialization failed"); return (-1); } smbd.s_door_srv = smbd_door_start(); if (smbd.s_door_srv < 0) { smbd_report("door initialization failed %s", strerror(errno)); return (-1); } dyndns_update_zones(); smbd_localtime_init(); (void) smb_lgrp_start(); smb_pwd_init(B_TRUE); if (smb_shr_start() != 0) { smbd_report("share initialization failed: %s", strerror(errno)); return (-1); } smbd.s_door_lmshr = smbd_share_start(); if (smbd.s_door_lmshr < 0) smbd_report("share initialization failed"); /* Open the driver, load the kernel config. */ if (smbd_kernel_bind() != 0) { return (-1); } smbd_load_shares(); smbd_load_printers(); smbd_spool_start(); smbd.s_initialized = B_TRUE; smbd_report("service initialized"); return (0); } /* * Shutdown smbd and smbsrv kernel services. * * Called only by the main thread. */ static void smbd_service_fini(void) { smbd.s_shutting_down = B_TRUE; smbd_report("service shutting down"); smb_kmod_stop(); smb_logon_abort(); smb_lgrp_stop(); smbd_pipesvc_stop(); smbd_door_stop(); smbd_authsvc_stop(); smbd_spool_stop(); smbd_kernel_unbind(); smbd_share_stop(); smb_shr_unload(); smb_shr_stop(); dyndns_stop(); smbd_nicmon_stop(); smb_ccache_remove(SMB_CCACHE_PATH); smb_pwd_fini(); smb_domain_fini(); mlsvc_fini(); smb_netbios_stop(); smbd_cups_fini(); smbd.s_initialized = B_FALSE; smbd_report("service terminated"); closelog(); } /* * Called when SMF sends us a SIGHUP. Update the smbd configuration * from SMF and check for changes that require service reconfiguration. */ static void smbd_refresh_handler() { int new_debug; if (smbd.s_shutting_down) return; smbd.s_refreshes++; new_debug = smb_config_get_debug(); if (smbd.s_debug || new_debug) smbd_report("debug=%d", new_debug); smbd.s_debug = new_debug; smbd_spool_stop(); smbd_dc_monitor_refresh(); smb_ccache_remove(SMB_CCACHE_PATH); /* * Clear the DNS zones for the existing interfaces * before updating the NIC interface list. */ dyndns_clear_zones(); if (smbd_nicmon_refresh() != 0) smbd_report("NIC monitor refresh failed"); smb_netbios_name_reconfig(); smb_browser_reconfig(); dyndns_update_zones(); /* This reloads the in-kernel config. */ (void) smbd_kernel_bind(); /* On refresh load share properties only, not the shares themselves */ smb_shr_load_execinfo(); smbd_load_printers(); smbd_spool_start(); } void smbd_set_secmode(int secmode) { switch (secmode) { case SMB_SECMODE_WORKGRP: case SMB_SECMODE_DOMAIN: (void) smb_config_set_secmode(secmode); smbd.s_secmode = secmode; break; default: syslog(LOG_ERR, "invalid security mode: %d", secmode); syslog(LOG_ERR, "entering maintenance mode"); (void) smb_smf_maintenance_mode(); } } /* * The service is online if initialization is complete and shutdown * has not begun. */ boolean_t smbd_online(void) { return (smbd.s_initialized && !smbd.s_shutting_down); } /* * Wait until the service is online. Provided for threads that * should wait until the service has been fully initialized before * they start performing operations. */ void smbd_online_wait(const char *text) { while (!smbd_online()) (void) sleep(SMBD_ONLINE_WAIT_INTERVAL); if (text != NULL) { syslog(LOG_DEBUG, "%s: online", text); (void) fprintf(stderr, "%s: online\n", text); } } /* * If the door has already been opened by another process (non-zero pid * in target), we assume that another smbd is already running. If there * is a race here, it will be caught later when smbsrv is opened because * only one process is allowed to open the device at a time. */ static int smbd_already_running(void) { door_info_t info; char *door_name; int door; door_name = getenv("SMBD_DOOR_NAME"); if (door_name == NULL) door_name = SMBD_DOOR_NAME; if ((door = open(door_name, O_RDONLY)) < 0) return (0); if (door_info(door, &info) < 0) return (0); if (info.di_target > 0) { smbd_report("already running: pid %ld\n", info.di_target); (void) close(door); return (1); } (void) close(door); return (0); } /* * smbd_kernel_bind * * If smbsrv is already bound, reload the configuration and update smbsrv. * Otherwise, open the smbsrv device and start the kernel service. */ static int smbd_kernel_bind(void) { smb_kmod_cfg_t cfg; int rc; if (smbd.s_kbound) { smb_load_kconfig(&cfg); smbd_get_authconf(&cfg); rc = smb_kmod_setcfg(&cfg); if (rc < 0) smbd_report("kernel configuration update failed: %s", strerror(rc)); return (rc); } if (smb_kmod_isbound()) smbd_kernel_unbind(); if ((rc = smb_kmod_bind(B_TRUE)) == 0) { rc = smbd_kernel_start(); if (rc != 0) smb_kmod_unbind(); else smbd.s_kbound = B_TRUE; } else { smbd_report("kernel bind error: %s", strerror(rc)); } return (rc); } static int smbd_kernel_start(void) { smb_kmod_cfg_t cfg; int rc; smb_load_kconfig(&cfg); smbd_get_authconf(&cfg); rc = smb_kmod_setcfg(&cfg); if (rc != 0) { smbd_report("kernel config ioctl error: %s", strerror(rc)); return (rc); } rc = smb_kmod_setgmtoff(smbd_gmtoff()); if (rc != 0) { smbd_report("kernel gmtoff ioctl error: %s", strerror(rc)); return (rc); } rc = smb_kmod_start(smbd.s_door_opipe, smbd.s_door_lmshr, smbd.s_door_srv); if (rc != 0) { smbd_report("kernel start ioctl error: %s", strerror(rc)); return (rc); } return (0); } /* * smbd_kernel_unbind */ static void smbd_kernel_unbind(void) { smb_kmod_unbind(); smbd.s_kbound = B_FALSE; } /* * Create the Dynamic DNS publisher thread. */ static void smbd_dyndns_init(void) { pthread_t tid; pthread_attr_t attr; int rc; dyndns_start(); (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&tid, &attr, dyndns_publisher, NULL); (void) pthread_attr_destroy(&attr); if (rc != 0) smbd_report("unable to start dyndns publisher: %s", strerror(errno)); } /* * Launches a thread to populate the share cache by share information * stored in sharemgr */ static void smbd_load_shares(void) { pthread_t tid; pthread_attr_t attr; int rc; (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&tid, &attr, smbd_share_loader, NULL); (void) pthread_attr_destroy(&attr); if (rc != 0) smbd_report("unable to load disk shares: %s", strerror(errno)); } /* * This wrapper function is used to avoid casting smb_shr_load() in * pthread_create() above. It is called very infrequently. */ static void * smbd_share_loader(void *args) { (void) smb_shr_load(args); return (NULL); } /* * Initialization of the localtime thread. * Returns 0 on success, an error number if thread creation fails. */ static void smbd_localtime_init(void) { pthread_attr_t attr; int rc; (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&smbd.s_localtime_tid, &attr, smbd_localtime_monitor, NULL); (void) pthread_attr_destroy(&attr); if (rc != 0) smbd_report("unable to monitor localtime: %s", strerror(errno)); } /* * Send local gmtoff to the kernel module one time at startup and each * time it changes (up to twice a year). * Local gmtoff is checked once every 15 minutes since some timezones * are aligned on half and quarter hour boundaries. */ /*ARGSUSED*/ static void * smbd_localtime_monitor(void *arg) { struct tm local_tm; time_t secs; int32_t gmtoff, last_gmtoff = -1; int timeout; int error; smbd_online_wait("smbd_localtime_monitor"); for (;;) { gmtoff = smbd_gmtoff(); if ((last_gmtoff != gmtoff) && smbd.s_kbound) { error = smb_kmod_setgmtoff(gmtoff); if (error != 0) smbd_report("localtime set failed: %s", strerror(error)); } /* * Align the next iteration on a fifteen minute boundary. */ secs = time(0); (void) localtime_r(&secs, &local_tm); timeout = ((15 - (local_tm.tm_min % 15)) * SECSPERMIN); (void) sleep(timeout); last_gmtoff = gmtoff; } /*NOTREACHED*/ return (NULL); } /* * smbd_gmtoff * * Determine offset from GMT. If daylight saving time use altzone, * otherwise use timezone. */ static int32_t smbd_gmtoff(void) { time_t clock_val; struct tm *atm; int32_t gmtoff; (void) time(&clock_val); atm = localtime(&clock_val); gmtoff = (atm->tm_isdst) ? altzone : timezone; return (gmtoff); } /* * Set up configuration options and parse the command line. * This function will determine if we will run as a daemon * or in the foreground. * * Failure to find a uid or gid results in using the default (0). */ static int smbd_setup_options(int argc, char *argv[]) { struct passwd *pwd; struct group *grp; int c; if ((pwd = getpwnam("root")) != NULL) smbd.s_uid = pwd->pw_uid; if ((grp = getgrnam("sys")) != NULL) smbd.s_gid = grp->gr_gid; smbd.s_debug = smb_config_get_debug(); smbd.s_fg = smb_config_get_fg_flag(); while ((c = getopt(argc, argv, ":dfs")) != -1) { switch (c) { case 'd': smbd.s_debug++; break; case 'f': smbd.s_fg = 1; break; case 's': smbd.s_dbg_stop = 1; break; case ':': case '?': default: smbd_usage(stderr); return (-1); } } return (0); } static void smbd_usage(FILE *fp) { static char *help[] = { "-d enable debug messages" "-f run program in foreground" }; int i; (void) fprintf(fp, "Usage: %s [-f]\n", smbd.s_pname); for (i = 0; i < sizeof (help)/sizeof (help[0]); ++i) (void) fprintf(fp, " %s\n", help[i]); } void smbd_report(const char *fmt, ...) { char buf[128]; va_list ap; if (fmt == NULL) return; va_start(ap, fmt); (void) vsnprintf(buf, 128, fmt, ap); va_end(ap); (void) fprintf(stderr, "smbd: %s\n", buf); } /* * Once we're out of memory, we're not likely to recover * without a restart. Let SMF restart this service. */ void smbd_nomem(void) { smbd_report(strerror(ENOMEM)); if (smbd.s_debug) abort(); exit(1); } /* * Enable libumem debugging by default on DEBUG builds. */ #ifdef DEBUG const char * _umem_debug_init(void) { return ("default,verbose"); /* $UMEM_DEBUG setting */ } const char * _umem_logging_init(void) { return ("fail,contents"); /* $UMEM_LOGGING setting */ } #endif /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * smbd NIC monitor. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" #define SMBD_NICMON_ENABLE "nicmon_enable" #define SMBD_NICMON_THROTTLE 100 #define SMBD_NICMON_DEBOUNCE 2 extern smbd_t smbd; static boolean_t smbd_nicmon_enabled = B_TRUE; /* Use this to stop monitoring */ static int eventpipe_write = -1; /* Use this to refresh service instance */ static char *smbd_nicmon_caller_fmri = NULL; static void smbd_nicmon_run_check(void); static int smbd_nicmon_setup_rtsock(int); static int smbd_nicmon_needscan(int); static int smbd_nicmon_setup_eventpipe(int *, int *); static void *smbd_nicmon_daemon(void *); /* * Start the nic monitor thread. */ int smbd_nicmon_start(const char *svc_fmri) { pthread_t smbd_nicmon_tid; int rc; if (smb_nic_init() != SMB_NIC_SUCCESS) return (-1); rc = pthread_create(&smbd_nicmon_tid, NULL, smbd_nicmon_daemon, NULL); if (rc != 0) return (-1); if (svc_fmri) smbd_nicmon_caller_fmri = (char *)svc_fmri; smbd_nicmon_run_check(); return (0); } void smbd_nicmon_stop(void) { uchar_t buf = 1; if (eventpipe_write < 0) return; (void) write(eventpipe_write, &buf, sizeof (buf)); smbd_nicmon_caller_fmri = NULL; smb_nic_fini(); } int smbd_nicmon_refresh(void) { if (smb_nic_init() != SMB_NIC_SUCCESS) return (-1); smbd_nicmon_run_check(); return (0); } /* * The monitor is enabled unless it is explicitly * disabled by setting smbd/nicmon_enable to false. * smbd/nicmon_enable is not defined by default. */ static void smbd_nicmon_run_check(void) { smb_scfhandle_t *hd; uint8_t status; int rc; smbd_nicmon_enabled = B_TRUE; if ((hd = smb_smf_scf_init(SMBD_FMRI_PREFIX)) == NULL) { syslog(LOG_DEBUG, "smbd_nicmon: smb_smf_scf_init failed"); return; } rc = smb_smf_create_service_pgroup(hd, SMBD_PG_NAME); if (rc != SMBD_SMF_OK) { smb_smf_scf_fini(hd); syslog(LOG_DEBUG, "smbd_nicmon: smb_smf_create_service_pgroup failed"); return; } rc = smb_smf_get_boolean_property(hd, SMBD_NICMON_ENABLE, &status); if (rc == SMBD_SMF_OK && status == 0) smbd_nicmon_enabled = B_FALSE; smb_smf_scf_fini(hd); } /* * Setup routing socket for getting RTM messages. */ static int smbd_nicmon_setup_rtsock(int af) { int sd; int flags; if ((sd = socket(PF_ROUTE, SOCK_RAW, af)) == -1) { syslog(LOG_ERR, "smbd_nicmon: routing socket failed: %d", errno); return (-1); } if ((flags = fcntl(sd, F_GETFL, 0)) < 0) { syslog(LOG_ERR, "smbd_nicmon: fcntl F_GETFL failed: %d", errno); (void) close(sd); return (-1); } if ((fcntl(sd, F_SETFL, flags | O_NONBLOCK)) < 0) { syslog(LOG_ERR, "smbd_nicmon: fcntl F_SETFL failed: %d", errno); (void) close(sd); return (-1); } return (sd); } static int smbd_nicmon_needscan(int sock) { static uint32_t throttle; struct rt_msghdr *rtm; int64_t msg[2048 / 8]; int need_if_scan = 0; int nbytes; /* Read as many messages as possible and try to empty the sockets */ for (;;) { nbytes = read(sock, msg, sizeof (msg)); if (nbytes <= 0) break; rtm = (struct rt_msghdr *)msg; if (rtm->rtm_version != RTM_VERSION) continue; if (nbytes < rtm->rtm_msglen) { if ((throttle % SMBD_NICMON_THROTTLE) == 0) { syslog(LOG_DEBUG, "smbd_nicmon: short read: %d of %d", nbytes, rtm->rtm_msglen); } ++throttle; continue; } switch (rtm->rtm_type) { case RTM_NEWADDR: case RTM_DELADDR: case RTM_IFINFO: need_if_scan = 1; break; default: break; } } return (need_if_scan); } /* * Create pipe for signal delivery and set up signal handlers. */ static int smbd_nicmon_setup_eventpipe(int *read_pipe, int *write_pipe) { int fds[2]; if ((pipe(fds)) < 0) { syslog(LOG_ERR, "smbd_nicmon: event pipe failed: %d", errno); return (-1); } *read_pipe = fds[0]; *write_pipe = fds[1]; return (0); } /* * Create the global routing socket to monitor changes in NIC interfaces. * We are only interested in new inerface addition/deletion and changes * in UP/DOWN status. * * Note: only supports AF_INET routing socket. Need to add AF_INET6 to * support IPv6. */ /*ARGSUSED*/ static void * smbd_nicmon_daemon(void *arg) { static uint32_t throttle; static int rtsock_v4; static int eventpipe_read = -1; struct pollfd pollfds[2]; int pollfd_num = 2; int i, nic_changed; int rc; if ((rtsock_v4 = smbd_nicmon_setup_rtsock(AF_INET)) == -1) return (NULL); rc = smbd_nicmon_setup_eventpipe(&eventpipe_read, &eventpipe_write); if (rc != 0) return (NULL); /* * Listen for activity on any of the sockets. * The delay before checking the rtsock will hopefully * smooth things out when there is a lot of activity. */ for (;;) { errno = 0; nic_changed = 0; pollfds[0].fd = rtsock_v4; pollfds[0].events = POLLIN; pollfds[1].fd = eventpipe_read; pollfds[1].events = POLLIN; if (poll(pollfds, pollfd_num, -1) < 0) { if (errno == EINTR) continue; if ((throttle % SMBD_NICMON_THROTTLE) == 0) syslog(LOG_DEBUG, "smbd_nicmon: poll failed: %d", errno); ++throttle; break; } for (i = 0; i < pollfd_num; i++) { if ((pollfds[i].fd < 0) || !(pollfds[i].revents & POLLIN)) continue; if (pollfds[i].fd == rtsock_v4) { (void) sleep(SMBD_NICMON_DEBOUNCE); nic_changed = smbd_nicmon_needscan(rtsock_v4); } if (pollfds[i].fd == eventpipe_read) goto done; } /* * If the monitor is enabled and something has changed, * refresh the registered SMF service. */ if (smbd_nicmon_enabled && nic_changed && smbd_nicmon_caller_fmri) { if (smf_refresh_instance(smbd_nicmon_caller_fmri) != 0) syslog(LOG_ERR, "smbd_nicmon: %s refresh failed", smbd_nicmon_caller_fmri); } } done: (void) close(rtsock_v4); (void) close(eventpipe_read); (void) close(eventpipe_write); eventpipe_write = -1; return (NULL); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * SPNEGO back-end for NTLMSSP. See [MS-NLMP] */ #include #include #include #include "smbd.h" #include "smbd_authsvc.h" #include "netsmb/ntlmssp.h" #include /* A shorter alias for a crazy long name from [MS-NLMP] */ #define NTLMSSP_NEGOTIATE_NTLM2 \ NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY /* Need this in a header somewhere */ #ifdef _LITTLE_ENDIAN /* little-endian values on little-endian */ #define htolel(x) ((uint32_t)(x)) #define letohl(x) ((uint32_t)(x)) #else /* (BYTE_ORDER == LITTLE_ENDIAN) */ /* little-endian values on big-endian (swap) */ #define letohl(x) BSWAP_32(x) #define htolel(x) BSWAP_32(x) #endif /* (BYTE_ORDER == LITTLE_ENDIAN) */ typedef struct ntlmssp_backend { uint32_t expect_type; uint32_t clnt_flags; uint32_t srv_flags; char srv_challenge[8]; } ntlmssp_backend_t; struct genhdr { char h_id[8]; /* "NTLMSSP" */ uint32_t h_type; }; struct sec_buf { uint16_t sb_length; uint16_t sb_maxlen; uint32_t sb_offset; }; struct nego_hdr { char h_id[8]; uint32_t h_type; uint32_t h_flags; /* workstation domain, name (place holders) */ uint16_t ws_dom[4]; uint16_t ws_name[4]; }; struct auth_hdr { char h_id[8]; uint32_t h_type; struct sec_buf h_lm_resp; struct sec_buf h_nt_resp; struct sec_buf h_domain; struct sec_buf h_user; struct sec_buf h_wksta; struct sec_buf h_essn_key; /* encrypted session key */ uint32_t h_flags; /* Version struct (optional) */ /* MIC hash (optional) */ }; /* Allow turning these off for debugging, etc. */ int smbd_signing_enabled = 1; int smbd_constant_challenge = 0; static uint8_t constant_chal[8] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 }; static int smbd_ntlmssp_negotiate(authsvc_context_t *); static int smbd_ntlmssp_authenticate(authsvc_context_t *); static int encode_avpair_str(smb_msgbuf_t *, uint16_t, char *); static int decode_secbuf_bin(smb_msgbuf_t *, struct sec_buf *, void **); static int decode_secbuf_str(smb_msgbuf_t *, struct sec_buf *, char **); /* * Initialize this context for NTLMSSP, if possible. */ int smbd_ntlmssp_init(authsvc_context_t *ctx) { ntlmssp_backend_t *be; be = malloc(sizeof (*be)); if (be == 0) return (NT_STATUS_NO_MEMORY); bzero(be, sizeof (*be)); be->expect_type = NTLMSSP_MSGTYPE_NEGOTIATE; ctx->ctx_backend = be; return (0); } void smbd_ntlmssp_fini(authsvc_context_t *ctx) { free(ctx->ctx_backend); } /* * Handle an auth message */ int smbd_ntlmssp_work(authsvc_context_t *ctx) { struct genhdr *ihdr = ctx->ctx_ibodybuf; ntlmssp_backend_t *be = ctx->ctx_backend; uint32_t mtype; int rc; if (ctx->ctx_ibodylen < sizeof (*ihdr)) return (NT_STATUS_INVALID_PARAMETER); if (bcmp(ihdr->h_id, "NTLMSSP", 8)) return (NT_STATUS_INVALID_PARAMETER); mtype = letohl(ihdr->h_type); if (mtype != be->expect_type) return (NT_STATUS_INVALID_PARAMETER); switch (mtype) { case NTLMSSP_MSGTYPE_NEGOTIATE: ctx->ctx_orawtype = LSA_MTYPE_ES_CONT; rc = smbd_ntlmssp_negotiate(ctx); break; case NTLMSSP_MSGTYPE_AUTHENTICATE: ctx->ctx_orawtype = LSA_MTYPE_ES_DONE; rc = smbd_ntlmssp_authenticate(ctx); break; default: case NTLMSSP_MSGTYPE_CHALLENGE: /* Sent by servers, not received. */ rc = NT_STATUS_INVALID_PARAMETER; break; } return (rc); } #if (MAXHOSTNAMELEN < NETBIOS_NAME_SZ) #error "MAXHOSTNAMELEN < NETBIOS_NAME_SZ" #endif /* * Handle an NTLMSSP_MSGTYPE_NEGOTIATE message, and reply * with an NTLMSSP_MSGTYPE_CHALLENGE message. * See: [MS-NLMP] 2.2.1.1, 3.2.5.1.1 */ static int smbd_ntlmssp_negotiate(authsvc_context_t *ctx) { char tmp_name[MAXHOSTNAMELEN]; ntlmssp_backend_t *be = ctx->ctx_backend; struct nego_hdr *ihdr = ctx->ctx_ibodybuf; smb_msgbuf_t mb; uint8_t *save_scan; int secmode; int mbflags; int rc; size_t var_start, var_end; uint16_t var_size; if (ctx->ctx_ibodylen < sizeof (*ihdr)) return (NT_STATUS_INVALID_PARAMETER); be->clnt_flags = letohl(ihdr->h_flags); /* * Looks like we can ignore ws_dom, ws_name. * Otherwise would parse those here. */ secmode = smb_config_get_secmode(); if (smbd_constant_challenge) { (void) memcpy(be->srv_challenge, constant_chal, sizeof (be->srv_challenge)); } else { randomize(be->srv_challenge, sizeof (be->srv_challenge)); } /* * Compute srv_flags */ be->srv_flags = NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO; be->srv_flags |= be->clnt_flags & ( NTLMSSP_NEGOTIATE_NTLM2 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_56); if (smbd_signing_enabled) { be->srv_flags |= be->clnt_flags & ( NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_ALWAYS_SIGN); } if (be->clnt_flags & NTLMSSP_NEGOTIATE_UNICODE) be->srv_flags |= NTLMSSP_NEGOTIATE_UNICODE; else if (be->clnt_flags & NTLMSSP_NEGOTIATE_OEM) be->srv_flags |= NTLMSSP_NEGOTIATE_OEM; /* LM Key is mutually exclusive with NTLM2 */ if ((be->srv_flags & NTLMSSP_NEGOTIATE_NTLM2) == 0 && (be->clnt_flags & NTLMSSP_NEGOTIATE_LM_KEY) != 0) be->srv_flags |= NTLMSSP_NEGOTIATE_LM_KEY; /* Get our "target name" */ if (secmode == SMB_SECMODE_DOMAIN) { be->srv_flags |= NTLMSSP_TARGET_TYPE_DOMAIN; rc = smb_getdomainname(tmp_name, NETBIOS_NAME_SZ); } else { be->srv_flags |= NTLMSSP_TARGET_TYPE_SERVER; rc = smb_getnetbiosname(tmp_name, NETBIOS_NAME_SZ); } if (rc) goto errout; /* * Build the NTLMSSP_MSGTYPE_CHALLENGE message. */ mbflags = SMB_MSGBUF_NOTERM; if (be->srv_flags & NTLMSSP_NEGOTIATE_UNICODE) mbflags |= SMB_MSGBUF_UNICODE; smb_msgbuf_init(&mb, ctx->ctx_obodybuf, ctx->ctx_obodylen, mbflags); /* * Fixed size parts */ rc = smb_msgbuf_encode( &mb, "8clwwll8cllwwl", /* offset, name (fmt) */ "NTLMSSP", /* 0: signature (8c) */ NTLMSSP_MSGTYPE_CHALLENGE, /* 8: type (l) */ 0, 0, 0, /* filled later: 12: target name (wwl) */ be->srv_flags, /* 20: flags (l) */ be->srv_challenge, /* 24: (8c) */ 0, 0, /* 32: reserved (ll) */ 0, 0, 0); /* filled later: 40: target info (wwl) */ #define TARGET_NAME_OFFSET 12 #define TARGET_INFO_OFFSET 40 if (rc < 0) goto errout; /* * Variable length parts. * * Target name */ var_start = smb_msgbuf_used(&mb); rc = smb_msgbuf_encode(&mb, "u", tmp_name); var_end = smb_msgbuf_used(&mb); var_size = (uint16_t)(var_end - var_start); if (rc < 0) goto errout; /* overwrite target name offset+lengths */ save_scan = mb.scan; mb.scan = mb.base + TARGET_NAME_OFFSET; (void) smb_msgbuf_encode(&mb, "wwl", var_size, var_size, var_start); mb.scan = save_scan; /* * Target info (AvPairList) * * These AV pairs are like our name/value pairs, but have * numeric identifiers instead of names. There are many * of these, but we put only the four expected by Windows: * NetBIOS computer name * NetBIOS domain name * DNS computer name * DNS domain name * Note that "domain" above (even "DNS domain") refers to * the AD domain of which we're a member, which may be * _different_ from the configured DNS domain. * * Also note that in "workgroup" mode (not a domain member) * all "domain" fields should be set to the same values as * the "computer" fields ("bare" host name, not FQDN). */ var_start = smb_msgbuf_used(&mb); /* NetBIOS Computer Name */ if (smb_getnetbiosname(tmp_name, NETBIOS_NAME_SZ)) goto errout; if (encode_avpair_str(&mb, MsvAvNbComputerName, tmp_name) < 0) goto errout; if (secmode != SMB_SECMODE_DOMAIN) { /* * Workgroup mode. Set all to hostname. * tmp_name = netbios hostname from above. */ if (encode_avpair_str(&mb, MsvAvNbDomainName, tmp_name) < 0) goto errout; /* * Want the bare computer name here (not FQDN). */ if (smb_gethostname(tmp_name, MAXHOSTNAMELEN, SMB_CASE_LOWER)) goto errout; if (encode_avpair_str(&mb, MsvAvDnsComputerName, tmp_name) < 0) goto errout; if (encode_avpair_str(&mb, MsvAvDnsDomainName, tmp_name) < 0) goto errout; } else { /* * Domain mode. Use real host and domain values. */ /* NetBIOS Domain Name */ if (smb_getdomainname(tmp_name, NETBIOS_NAME_SZ)) goto errout; if (encode_avpair_str(&mb, MsvAvNbDomainName, tmp_name) < 0) goto errout; /* DNS Computer Name */ if (smb_getfqhostname(tmp_name, MAXHOSTNAMELEN)) goto errout; if (encode_avpair_str(&mb, MsvAvDnsComputerName, tmp_name) < 0) goto errout; /* DNS Domain Name */ if (smb_getfqdomainname(tmp_name, MAXHOSTNAMELEN)) goto errout; if (encode_avpair_str(&mb, MsvAvDnsDomainName, tmp_name) < 0) goto errout; } /* End marker */ if (smb_msgbuf_encode(&mb, "ww", MsvAvEOL, 0) < 0) goto errout; var_end = smb_msgbuf_used(&mb); var_size = (uint16_t)(var_end - var_start); /* overwrite target offset+lengths */ save_scan = mb.scan; mb.scan = mb.base + TARGET_INFO_OFFSET; (void) smb_msgbuf_encode(&mb, "wwl", var_size, var_size, var_start); mb.scan = save_scan; ctx->ctx_obodylen = smb_msgbuf_used(&mb); smb_msgbuf_term(&mb); be->expect_type = NTLMSSP_MSGTYPE_AUTHENTICATE; return (0); errout: smb_msgbuf_term(&mb); return (NT_STATUS_INTERNAL_ERROR); } static int encode_avpair_str(smb_msgbuf_t *mb, uint16_t AvId, char *name) { int rc; uint16_t len; len = smb_wcequiv_strlen(name); rc = smb_msgbuf_encode(mb, "wwU", AvId, len, name); return (rc); } /* * Handle an NTLMSSP_MSGTYPE_AUTHENTICATE message. * See: [MS-NLMP] 2.2.1.3, 3.2.5.1.2 */ static int smbd_ntlmssp_authenticate(authsvc_context_t *ctx) { struct auth_hdr hdr; smb_msgbuf_t mb; smb_logon_t user_info; smb_token_t *token = NULL; ntlmssp_backend_t *be = ctx->ctx_backend; void *lm_resp; void *nt_resp; char *domain; char *user; char *wksta; void *essn_key; /* encrypted session key (optional) */ int mbflags; uint_t status = NT_STATUS_INTERNAL_ERROR; char combined_challenge[SMBAUTH_CHAL_SZ]; unsigned char kxkey[SMBAUTH_HASH_SZ]; boolean_t ntlm_v1x = B_FALSE; bzero(&user_info, sizeof (user_info)); /* * Parse the NTLMSSP_MSGTYPE_AUTHENTICATE message. */ if (ctx->ctx_ibodylen < sizeof (hdr)) return (NT_STATUS_INVALID_PARAMETER); mbflags = SMB_MSGBUF_NOTERM; if (be->srv_flags & NTLMSSP_NEGOTIATE_UNICODE) mbflags |= SMB_MSGBUF_UNICODE; smb_msgbuf_init(&mb, ctx->ctx_ibodybuf, ctx->ctx_ibodylen, mbflags); bzero(&hdr, sizeof (hdr)); if (smb_msgbuf_decode(&mb, "12.") < 0) goto errout; if (decode_secbuf_bin(&mb, &hdr.h_lm_resp, &lm_resp) < 0) goto errout; if (decode_secbuf_bin(&mb, &hdr.h_nt_resp, &nt_resp) < 0) goto errout; if (decode_secbuf_str(&mb, &hdr.h_domain, &domain) < 0) goto errout; if (decode_secbuf_str(&mb, &hdr.h_user, &user) < 0) goto errout; if (decode_secbuf_str(&mb, &hdr.h_wksta, &wksta) < 0) goto errout; if (decode_secbuf_bin(&mb, &hdr.h_essn_key, &essn_key) < 0) goto errout; if (smb_msgbuf_decode(&mb, "l", &be->clnt_flags) < 0) goto errout; if (be->clnt_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { if (hdr.h_essn_key.sb_length < 16 || essn_key == NULL) goto errout; } user_info.lg_level = NETR_NETWORK_LOGON; user_info.lg_flags = 0; user_info.lg_ntlm_flags = be->clnt_flags; user_info.lg_username = (user) ? user : ""; user_info.lg_domain = (domain) ? domain : ""; user_info.lg_workstation = (wksta) ? wksta : ""; user_info.lg_clnt_ipaddr = ctx->ctx_clinfo.lci_clnt_ipaddr; user_info.lg_local_port = 445; user_info.lg_challenge_key.len = SMBAUTH_CHAL_SZ; user_info.lg_challenge_key.val = (uint8_t *)be->srv_challenge; user_info.lg_nt_password.len = hdr.h_nt_resp.sb_length; user_info.lg_nt_password.val = nt_resp; user_info.lg_lm_password.len = hdr.h_lm_resp.sb_length; user_info.lg_lm_password.val = lm_resp; user_info.lg_native_os = ctx->ctx_clinfo.lci_native_os; user_info.lg_native_lm = ctx->ctx_clinfo.lci_native_lm; /* * If we're doing extended session security, the challenge * this OWF was computed with is different. [MS-NLMP 3.3.1] * It's: MD5(concat(ServerChallenge,ClientChallenge)) * where the ClientChallenge is in the LM resp. field. */ if (user_info.lg_nt_password.len == SMBAUTH_LM_RESP_SZ && user_info.lg_lm_password.len >= SMBAUTH_CHAL_SZ && (be->clnt_flags & NTLMSSP_NEGOTIATE_NTLM2) != 0) { smb_auth_ntlm2_mkchallenge(combined_challenge, be->srv_challenge, lm_resp); user_info.lg_challenge_key.val = (uint8_t *)combined_challenge; user_info.lg_lm_password.len = 0; ntlm_v1x = B_TRUE; } /* * This (indirectly) calls smb_auth_validate() to * check that the client gave us a valid hash. */ token = smbd_user_auth_logon(&user_info); if (token == NULL) { status = user_info.lg_status; if (status == 0) /* should not happen */ status = NT_STATUS_INTERNAL_ERROR; goto errout; } if (token->tkn_ssnkey.val != NULL && token->tkn_ssnkey.len == SMBAUTH_HASH_SZ) { /* * At this point, token->tkn_session_key is the * "Session Base Key" [MS-NLMP] 3.2.5.1.2 * Compute the final session key. First need the * "Key Exchange Key" [MS-NLMP] 3.4.5.1 */ if (ntlm_v1x) { smb_auth_ntlm2_kxkey(kxkey, be->srv_challenge, lm_resp, token->tkn_ssnkey.val); } else { /* KXKEY is the Session Base Key. */ (void) memcpy(kxkey, token->tkn_ssnkey.val, SMBAUTH_HASH_SZ); } /* * If the client give us an encrypted session key, * decrypt it (RC4) using the "key exchange key". */ if (be->clnt_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { /* RC4 args: result, key, data */ (void) smb_auth_RC4(token->tkn_ssnkey.val, SMBAUTH_HASH_SZ, kxkey, SMBAUTH_HASH_SZ, essn_key, hdr.h_essn_key.sb_length); } else { /* Final key is the KXKEY */ (void) memcpy(token->tkn_ssnkey.val, kxkey, SMBAUTH_HASH_SZ); } } ctx->ctx_token = token; ctx->ctx_obodylen = 0; smb_msgbuf_term(&mb); return (0); errout: smb_msgbuf_term(&mb); return (status); } static int decode_secbuf_bin(smb_msgbuf_t *mb, struct sec_buf *sb, void **binp) { int rc; *binp = NULL; rc = smb_msgbuf_decode( mb, "wwl", &sb->sb_length, &sb->sb_maxlen, &sb->sb_offset); if (rc < 0) return (rc); if (sb->sb_offset > mb->max) return (SMB_MSGBUF_UNDERFLOW); if (sb->sb_length > (mb->max - sb->sb_offset)) return (SMB_MSGBUF_UNDERFLOW); if (sb->sb_length == 0) return (rc); *binp = mb->base + sb->sb_offset; return (0); } static int decode_secbuf_str(smb_msgbuf_t *mb, struct sec_buf *sb, char **cpp) { uint8_t *save_scan; int rc; *cpp = NULL; rc = smb_msgbuf_decode( mb, "wwl", &sb->sb_length, &sb->sb_maxlen, &sb->sb_offset); if (rc < 0) return (rc); if (sb->sb_offset > mb->max) return (SMB_MSGBUF_UNDERFLOW); if (sb->sb_length > (mb->max - sb->sb_offset)) return (SMB_MSGBUF_UNDERFLOW); if (sb->sb_length == 0) return (rc); save_scan = mb->scan; mb->scan = mb->base + sb->sb_offset; rc = smb_msgbuf_decode(mb, "#u", (int)sb->sb_length, cpp); mb->scan = save_scan; return (rc); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright 2022-2023 RackTop Systems, Inc. */ /* * This is the named pipe service for smbd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" struct pipe_listener { const char *name; int max_allowed; int max_seen; int current; pthread_t tid; }; static void *pipesvc_listener(void *); static void *pipesvc_worker(void *); static int pipe_send(ndr_pipe_t *, void *, size_t); static int pipe_recv(ndr_pipe_t *, void *, size_t); mutex_t pipesvc_mutex = DEFAULTMUTEX; int pipesvc_workers_max = 500; int pipesvc_workers_cur = 0; uint16_t pipe_max_msgsize = SMB_PIPE_MAX_MSGSIZE; /* * Allow more opens on SRVSVC because that's used by many clients * to get the share list, etc. */ #define SRVSVC_MAX_OPENS 200 #define DEF_MAX_OPENS 50 #define NLISTENERS 11 static struct pipe_listener pipe_listeners[NLISTENERS] = { { "eventlog", DEF_MAX_OPENS, 0, 0 }, { "lsarpc", DEF_MAX_OPENS, 0, 0 }, { "lsass", DEF_MAX_OPENS, 0, 0 }, { "netdfs", DEF_MAX_OPENS, 0, 0 }, { "netlogon", DEF_MAX_OPENS, 0, 0 }, { "samr", DEF_MAX_OPENS, 0, 0 }, { "spoolss", DEF_MAX_OPENS, 0, 0 }, { "srvsvc", SRVSVC_MAX_OPENS, 0, 0 }, { "svcctl", DEF_MAX_OPENS, 0, 0 }, { "winreg", DEF_MAX_OPENS, 0, 0 }, { "wkssvc", DEF_MAX_OPENS, 0, 0 }, }; static ndr_pipe_t * np_new(struct pipe_listener *pl, int fid) { ndr_pipe_t *np; size_t len; /* * Allocating ndr_pipe_t + smb_netuserinfo_t as one. * We could just make that part of ndr_pipe_t, but * that struct is opaque to libmlrpc. */ len = sizeof (*np) + sizeof (smb_netuserinfo_t); np = malloc(len); if (np == NULL) return (NULL); bzero(np, len); np->np_listener = pl; np->np_endpoint = pl->name; np->np_user = (void*)(np + 1); np->np_send = pipe_send; np->np_recv = pipe_recv; np->np_fid = fid; np->np_max_xmit_frag = pipe_max_msgsize; np->np_max_recv_frag = pipe_max_msgsize; return (np); } static void np_free(ndr_pipe_t *np) { (void) close(np->np_fid); free(np); } /* * Create the smbd opipe door service. * Returns the door descriptor on success. Otherwise returns -1. */ int smbd_pipesvc_start(void) { pthread_t tid; pthread_attr_t tattr; struct pipe_listener *pl; int i, rc; if (mlsvc_init() != 0) { smbd_report("msrpc initialization failed"); return (-1); } (void) pthread_attr_init(&tattr); (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); for (i = 0; i < NLISTENERS; i++) { pl = &pipe_listeners[i]; pl->max_seen = 0; if (strcasecmp(pl->name, "spoolss") == 0 && smb_config_getbool(SMB_CI_PRINT_ENABLE) == B_FALSE) continue; rc = pthread_create(&tid, &tattr, pipesvc_listener, pl); if (rc != 0) break; pipe_listeners[i].tid = tid; } if (rc != 0) { smbd_report("pipesvc pthread_create, %d", rc); } (void) pthread_attr_destroy(&tattr); return (rc); } void smbd_pipesvc_stop(void) { int i; (void) mutex_lock(&pipesvc_mutex); for (i = 0; i < NLISTENERS; i++) { if (pipe_listeners[i].tid == 0) continue; (void) pthread_kill(pipe_listeners[i].tid, SIGTERM); pipe_listeners[i].tid = 0; } (void) mutex_unlock(&pipesvc_mutex); } static void * pipesvc_listener(void *varg) { struct sockaddr_un sa; int err, listen_fd, newfd, snlen; struct pipe_listener *pl = varg; ndr_pipe_t *np; pthread_t tid; int rc; listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (listen_fd < 0) { smbd_report("pipesvc_listener, so_create: %d", errno); return (NULL); } bzero(&sa, sizeof (sa)); sa.sun_family = AF_UNIX; (void) snprintf(sa.sun_path, sizeof (sa.sun_path), "%s/%s", SMB_PIPE_DIR, pl->name); /* Bind it to a listening name. */ (void) unlink(sa.sun_path); if (bind(listen_fd, (struct sockaddr *)&sa, sizeof (sa)) < 0) { smbd_report("pipesvc_listener, so_bind: %d", errno); (void) close(listen_fd); return (NULL); } if (listen(listen_fd, SOMAXCONN) < 0) { smbd_report("pipesvc_listener, listen: %d", errno); (void) close(listen_fd); return (NULL); } for (;;) { snlen = sizeof (sa); newfd = accept(listen_fd, (struct sockaddr *)&sa, &snlen); if (newfd < 0) { err = errno; switch (err) { case ECONNABORTED: continue; case EINTR: /* normal termination */ goto out; default: smbd_report("pipesvc_listener, " "accept failed: %d", errno); } smbd_report("pipesvc_listener, accept: %d", err); break; } np = np_new(pl, newfd); if (np == NULL) { smbd_report("pipesvc_listener, alloc1 failed"); (void) close(newfd); smbd_nomem(); } rc = pthread_create(&tid, NULL, pipesvc_worker, np); if (rc != 0) { smbd_report("pipesvc_listener, pthread_create: %d", errno); np_free(np); smbd_nomem(); } (void) pthread_detach(tid); /* Note: np_free in pipesvc_worker */ np = NULL; } out: (void) close(listen_fd); pl->tid = 0; return (NULL); } #ifndef FKSMBD /* * Decide whether we should trust the (in-band) user information * that the client sends us over the named pipe. The (in-kernel) * SMB service calls this with the credential of the logged-on * SMB user. The privileges are normally: * effective: basic,file_dac_search * inheritable: basic * permitted: basic,file_dac_search,sys_smb * limit: all * This tests the permitted set for the presence of PRIV_SYS_SMB, * which should only be granted to the SMB server. */ static boolean_t pipe_has_priv(ndr_pipe_t *np) { ucred_t *uc = NULL; const priv_set_t *ps = NULL; boolean_t ret = B_FALSE; pid_t clpid; if (getpeerucred(np->np_fid, &uc) != 0) { smbd_report("pipesvc: getpeerucred err %d", errno); return (B_FALSE); } clpid = ucred_getpid(uc); ps = ucred_getprivset(uc, PRIV_PERMITTED); if (ps == NULL) { smbd_report("pipesvc: ucred_getprivset failed"); goto out; } /* * Require sys_smb priv. */ if (priv_ismember(ps, PRIV_SYS_SMB)) { ret = B_TRUE; goto out; } if (smbd.s_debug) { smbd_report("pipesvc: non-privileged client " "PID = %d UID = %d", (int)clpid, ucred_getruid(uc)); } out: /* ps is free'd with the ucred */ if (uc != NULL) ucred_free(uc); return (ret); } #endif static void * pipesvc_worker(void *varg) { XDR xdrs; smb_pipehdr_t phdr; ndr_pipe_t *np = varg; struct pipe_listener *pl = np->np_listener; void *buf = NULL; uint32_t status; ssize_t rc; (void) mutex_lock(&pipesvc_mutex); if (pipesvc_workers_cur >= pipesvc_workers_max || pl->current >= pl->max_allowed) { (void) mutex_unlock(&pipesvc_mutex); status = NT_STATUS_PIPE_NOT_AVAILABLE; (void) send(np->np_fid, &status, sizeof (status), 0); goto out_free_np; } pipesvc_workers_cur++; pl->current++; if (pl->max_seen < pl->current) pl->max_seen = pl->current; (void) mutex_unlock(&pipesvc_mutex); /* * The smbsrv kmod sends us one initial message containing an * XDR encoded smb_netuserinfo_t that we read and decode here, * all unbeknownst to libmlrpc. * * Might be nice to enhance getpeerucred() so it can give us * all the info smb_netuserinfo_t carries, and then use that, * which would allow using a more generic RPC service. */ rc = pipe_recv(np, &phdr, sizeof (phdr)); if (rc != 0) { smbd_report("pipesvc_worker, recv1: %d", rc); goto out_decr; } if (phdr.ph_magic != SMB_PIPE_HDR_MAGIC || phdr.ph_uilen > 8192) { smbd_report("pipesvc_worker, bad hdr"); goto out_decr; } buf = malloc(phdr.ph_uilen); if (buf == NULL) { smbd_report("pipesvc_worker, alloc1 failed"); goto out_decr; } rc = pipe_recv(np, buf, phdr.ph_uilen); if (rc != 0) { smbd_report("pipesvc_worker, recv2: %d", rc); goto out_decr; } xdrmem_create(&xdrs, buf, phdr.ph_uilen, XDR_DECODE); if (!smb_netuserinfo_xdr(&xdrs, np->np_user)) { smbd_report("pipesvc_worker, bad uinfo"); goto out_free_buf; } /* * Don't trust the netuserinfo unless the client side * has the necessary privileges */ #ifndef FKSMBD if (!pipe_has_priv(np)) { np->np_user->ui_flags = SMB_ATF_ANON; } #endif /* * Later, could disallow opens of some pipes by * anonymous users, etc. For now, reply "OK". */ status = 0; rc = pipe_send(np, &status, sizeof (status)); if (rc != 0) { smbd_report("pipesvc_worker, send1: %d", rc); goto out_free_buf; } /* * Run the RPC service loop worker, which * returns when it sees the pipe close. */ ndr_pipe_worker(np); xdrs.x_op = XDR_FREE; (void) smb_netuserinfo_xdr(&xdrs, np->np_user); out_free_buf: free(buf); xdr_destroy(&xdrs); out_decr: (void) mutex_lock(&pipesvc_mutex); pipesvc_workers_cur--; pl->current--; (void) mutex_unlock(&pipesvc_mutex); out_free_np: /* Cleanup what came in by varg. */ (void) shutdown(np->np_fid, SHUT_RDWR); np_free(np); return (NULL); } /* * These are the transport get/put callback functions provided * via the ndr_pipe_t object to the libmlrpc`ndr_pipe_worker. * These are called only with known PDU sizes and should * loop as needed to transfer the entire message. */ static int pipe_recv(ndr_pipe_t *np, void *buf, size_t len) { int x; while (len > 0) { x = recv(np->np_fid, buf, len, 0); if (x < 0) return (errno); if (x == 0) return (EIO); buf = (char *)buf + x; len -= x; } return (0); } static int pipe_send(ndr_pipe_t *np, void *buf, size_t len) { int x; while (len > 0) { x = send(np->np_fid, buf, len, 0); if (x < 0) return (errno); if (x == 0) return (EIO); buf = (char *)buf + x; len -= x; } return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. * Copyright 2022 RackTop Systems, Inc. */ /* * LanMan share door server */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" #define SMB_SHARE_DSRV_VERSION 1 #define SMB_SHARE_DSRV_COOKIE ((void*)(0xdeadbeef^SMB_SHARE_DSRV_VERSION)) static int smb_share_dsrv_fd = -1; static pthread_mutex_t smb_share_dsrv_mtx = PTHREAD_MUTEX_INITIALIZER; static smbd_door_t smb_share_sdh; static void smbd_share_dispatch(void *, char *, size_t, door_desc_t *, uint_t); /* * Start the LanMan share door service. * Returns 0 on success. Otherwise, -1. */ int smbd_share_start(void) { int newfd; const char *door_name; (void) pthread_mutex_lock(&smb_share_dsrv_mtx); if (smb_share_dsrv_fd != -1) { syslog(LOG_ERR, "smbd_share_start: duplicate"); (void) pthread_mutex_unlock(&smb_share_dsrv_mtx); return (smb_share_dsrv_fd); } smbd_door_init(&smb_share_sdh, "share"); if ((smb_share_dsrv_fd = door_create(smbd_share_dispatch, SMB_SHARE_DSRV_COOKIE, (DOOR_UNREF | DOOR_REFUSE_DESC))) < 0) { syslog(LOG_ERR, "smbd_share_start: door_create: %s", strerror(errno)); (void) pthread_mutex_unlock(&smb_share_dsrv_mtx); return (-1); } door_name = getenv("SMB_SHARE_DNAME"); if (door_name == NULL) door_name = SMB_SHARE_DNAME; (void) unlink(door_name); if ((newfd = creat(door_name, 0644)) < 0) { syslog(LOG_ERR, "smbd_share_start: open: %s", strerror(errno)); (void) door_revoke(smb_share_dsrv_fd); smb_share_dsrv_fd = -1; (void) pthread_mutex_unlock(&smb_share_dsrv_mtx); return (-1); } (void) close(newfd); (void) fdetach(door_name); if (fattach(smb_share_dsrv_fd, door_name) < 0) { syslog(LOG_ERR, "smbd_share_start: fattach: %s", strerror(errno)); (void) door_revoke(smb_share_dsrv_fd); smb_share_dsrv_fd = -1; (void) pthread_mutex_unlock(&smb_share_dsrv_mtx); return (-1); } (void) pthread_mutex_unlock(&smb_share_dsrv_mtx); return (smb_share_dsrv_fd); } /* * Stop the LanMan share door service. */ void smbd_share_stop(void) { (void) pthread_mutex_lock(&smb_share_dsrv_mtx); smbd_door_fini(&smb_share_sdh); if (smb_share_dsrv_fd != -1) { const char *door_name; door_name = getenv("SMB_SHARE_DNAME"); if (door_name == NULL) door_name = SMB_SHARE_DNAME; (void) fdetach(door_name); (void) door_revoke(smb_share_dsrv_fd); smb_share_dsrv_fd = -1; } (void) pthread_mutex_unlock(&smb_share_dsrv_mtx); } static boolean_t have_priv_sys_smb(void) { ucred_t *uc = NULL; const priv_set_t *ps = NULL; boolean_t ret = B_FALSE; pid_t pid; if (door_ucred(&uc) != 0) { syslog(LOG_DEBUG, "%s: door_ucred failed", __func__); goto out; } /* * in-kernel callers have pid==0 * If we have pid zero, that's sufficient. * If not, allow with sys_smb priv (below) */ pid = ucred_getpid(uc); if (pid == 0) { ret = B_TRUE; goto out; } ps = ucred_getprivset(uc, PRIV_EFFECTIVE); if (ps == NULL) { syslog(LOG_DEBUG, "%s: ucred_getprivset failed", __func__); goto out; } if (priv_ismember(ps, PRIV_SYS_SMB)) { ret = B_TRUE; goto out; } syslog(LOG_DEBUG, "smbd_share_dispatch: missing privilege, " "PID = %d UID = %d", (int)pid, ucred_getruid(uc)); out: /* ps is free'd with the ucred */ if (uc != NULL) ucred_free(uc); return (ret); } /* * This function with which the LMSHARE door is associated * will invoke the appropriate CIFS share management function * based on the request type of the door call. */ /*ARGSUSED*/ static void smbd_share_dispatch(void *cookie, char *ptr, size_t size, door_desc_t *dp, uint_t n_desc) { uint32_t rc; int req_type; char buf[SMB_SHARE_DSIZE]; unsigned int used; smb_dr_ctx_t *dec_ctx; smb_dr_ctx_t *enc_ctx; unsigned int dec_status; unsigned int enc_status; char *sharename, *sharename2; smb_share_t lmshr_info; smb_shrlist_t lmshr_list; int offset; smbd_door_enter(&smb_share_sdh); if ((cookie != SMB_SHARE_DSRV_COOKIE) || (ptr == NULL) || (size < sizeof (uint32_t))) { smbd_door_return(&smb_share_sdh, NULL, 0, NULL, 0); } dec_ctx = smb_dr_decode_start(ptr, size); enc_ctx = smb_dr_encode_start(buf, sizeof (buf)); req_type = smb_dr_get_uint32(dec_ctx); if (req_type != SMB_SHROP_NUM_SHARES && req_type != SMB_SHROP_LIST && !have_priv_sys_smb()) { dec_status = EPERM; goto decode_error; } switch (req_type) { case SMB_SHROP_NUM_SHARES: if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) goto decode_error; rc = smb_shr_count(); smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS); smb_dr_put_uint32(enc_ctx, rc); break; case SMB_SHROP_DELETE: sharename = smb_dr_get_string(dec_ctx); if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) { smb_dr_free_string(sharename); goto decode_error; } rc = smb_shr_remove(sharename); smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS); smb_dr_put_uint32(enc_ctx, rc); smb_dr_free_string(sharename); break; case SMB_SHROP_RENAME: sharename = smb_dr_get_string(dec_ctx); sharename2 = smb_dr_get_string(dec_ctx); if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) { smb_dr_free_string(sharename); smb_dr_free_string(sharename2); goto decode_error; } rc = smb_shr_rename(sharename, sharename2); smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS); smb_dr_put_uint32(enc_ctx, rc); smb_dr_free_string(sharename); smb_dr_free_string(sharename2); break; case SMB_SHROP_ADD: smb_dr_get_share(dec_ctx, &lmshr_info); if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) goto decode_error; rc = smb_shr_add(&lmshr_info); smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS); smb_dr_put_uint32(enc_ctx, rc); smb_dr_put_share(enc_ctx, &lmshr_info); break; case SMB_SHROP_MODIFY: smb_dr_get_share(dec_ctx, &lmshr_info); if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) { goto decode_error; } rc = smb_shr_modify(&lmshr_info); smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS); smb_dr_put_uint32(enc_ctx, rc); break; case SMB_SHROP_LIST: offset = smb_dr_get_int32(dec_ctx); if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) goto decode_error; smb_shr_list(offset, &lmshr_list); smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS); smb_dr_put_buf(enc_ctx, (unsigned char *)&lmshr_list, sizeof (smb_shrlist_t)); break; default: dec_status = smb_dr_decode_finish(dec_ctx); goto decode_error; } if ((enc_status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { enc_ctx = smb_dr_encode_start(buf, sizeof (buf)); smb_dr_put_int32(enc_ctx, SMB_SHARE_DERROR); smb_dr_put_uint32(enc_ctx, enc_status); (void) smb_dr_encode_finish(enc_ctx, &used); } smbd_door_return(&smb_share_sdh, buf, used, NULL, 0); return; decode_error: smb_dr_put_int32(enc_ctx, SMB_SHARE_DERROR); smb_dr_put_uint32(enc_ctx, dec_status); (void) smb_dr_encode_finish(enc_ctx, &used); smbd_door_return(&smb_share_sdh, buf, used, NULL, 0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* * CUPS support for the SMB and SPOOLSS print services. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" #ifdef HAVE_CUPS #include #define SMB_SPOOL_WAIT 2 #define SMBD_PJOBLEN 256 #define SMBD_PRINTER "Postscript" #define SMBD_FN_PREFIX "cifsprintjob-" #define SMBD_CUPS_SPOOL_DIR "//var//spool//cups" #define SMBD_CUPS_DOCNAME "generic_doc" typedef struct smbd_printjob { pid_t pj_pid; int pj_sysjob; int pj_fd; time_t pj_start_time; int pj_status; size_t pj_size; int pj_page_count; boolean_t pj_isspooled; boolean_t pj_jobnum; char pj_filename[SMBD_PJOBLEN]; char pj_jobname[SMBD_PJOBLEN]; char pj_username[SMBD_PJOBLEN]; char pj_queuename[SMBD_PJOBLEN]; } smbd_printjob_t; typedef struct smb_cups_ops { void *cups_hdl; cups_lang_t *(*cupsLangDefault)(); const char *(*cupsLangEncoding)(cups_lang_t *); void (*cupsLangFree)(cups_lang_t *); ipp_status_t (*cupsLastError)(); int (*cupsGetDests)(cups_dest_t **); void (*cupsFreeDests)(int, cups_dest_t *); ipp_t *(*cupsDoFileRequest)(http_t *, ipp_t *, const char *, const char *); ipp_t *(*ippNewRequest)(); void (*ippDelete)(); char *(*ippErrorString)(); ipp_attribute_t *(*ippAddString)(); void (*httpClose)(http_t *); http_t *(*httpConnect)(const char *, int); ipp_status_t (*ippGetStatusCode)(ipp_t *); } smb_cups_ops_t; static uint32_t smbd_cups_jobnum = 1; static smb_cups_ops_t smb_cups; static mutex_t smbd_cups_mutex; static void *smbd_spool_monitor(void *); static smb_cups_ops_t *smbd_cups_ops(void); static void smbd_print_share_comment(smb_share_t *, cups_dest_t *); static void *smbd_share_printers(void *); static void smbd_spool_copyfile(smb_inaddr_t *, char *, char *, char *); extern smbd_t smbd; /* * Start the spool thread. * Returns 0 on success, an error number if thread creation fails. */ void smbd_spool_start(void) { pthread_attr_t attr; int rc; if (!smb_config_getbool(SMB_CI_PRINT_ENABLE)) return; (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&smbd.s_spool_tid, &attr, smbd_spool_monitor, NULL); (void) pthread_attr_destroy(&attr); if (rc != 0) syslog(LOG_NOTICE, "failed to start print monitor: %s", strerror(errno)); } /* * A single pthread_kill should be sufficient but we include * a couple of retries to avoid implementation idiosyncrasies * around signal delivery. */ void smbd_spool_stop(void) { int i; if (pthread_self() == smbd.s_spool_tid) return; for (i = 0; i < 3 && smbd.s_spool_tid != 0; ++i) { if (pthread_kill(smbd.s_spool_tid, SIGTERM) == ESRCH) break; (void) sleep(1); } } /* * This thread blocks waiting for close print file in the kernel. * It then uses the data returned from the ioctl to copy the spool file * into the cups spooler. * * This mechanism is really only used by Windows Vista and Windows 7. * Other versions of Windows create a zero size file, which is removed * by smbd_spool_copyfile. */ /*ARGSUSED*/ static void * smbd_spool_monitor(void *arg) { uint32_t spool_num; char username[MAXNAMELEN]; char path[MAXPATHLEN]; smb_inaddr_t ipaddr; int error_retry_cnt = 5; smbd_online_wait("smbd_spool_monitor"); spoolss_register_copyfile(smbd_spool_copyfile); while (!smbd.s_shutting_down && (error_retry_cnt > 0)) { errno = 0; if (smb_kmod_get_spool_doc(&spool_num, username, path, &ipaddr) == 0) { smbd_spool_copyfile(&ipaddr, username, path, SMBD_CUPS_DOCNAME); error_retry_cnt = 5; } else { if (errno == ECANCELED) break; if ((errno != EINTR) && (errno != EAGAIN)) error_retry_cnt--; (void) sleep(SMB_SPOOL_WAIT); } } spoolss_register_copyfile(NULL); smbd.s_spool_tid = 0; return (NULL); } /* * All versions of windows use this function to spool files to a printer * via the cups interface */ static void smbd_spool_copyfile(smb_inaddr_t *ipaddr, char *username, char *path, char *doc_name) { smb_cups_ops_t *cups; http_t *http = NULL; /* HTTP connection to server */ ipp_t *request = NULL; /* IPP Request */ ipp_t *response = NULL; /* IPP Response */ char uri[HTTP_MAX_URI]; /* printer-uri attribute */ char new_jobname[SMBD_PJOBLEN]; smbd_printjob_t pjob; char clientname[INET6_ADDRSTRLEN]; struct stat sbuf; int rc = 1; if (stat(path, &sbuf)) { syslog(LOG_INFO, "smbd_spool_copyfile: %s: %s", path, strerror(errno)); return; } /* * Remove zero size files and return; these were inadvertantly * created by XP or 2000. */ if (sbuf.st_size == 0) { if (remove(path) != 0) syslog(LOG_INFO, "smbd_spool_copyfile: cannot remove %s: %s", path, strerror(errno)); return; } if ((cups = smbd_cups_ops()) == NULL) return; if ((http = cups->httpConnect("localhost", 631)) == NULL) { syslog(LOG_INFO, "smbd_spool_copyfile: cupsd not running"); return; } if ((request = cups->ippNewRequest(IPP_PRINT_JOB)) == NULL) { syslog(LOG_INFO, "smbd_spool_copyfile: ipp not running"); return; } (void) snprintf(uri, sizeof (uri), "ipp://localhost/printers/%s", SMBD_PRINTER); pjob.pj_pid = pthread_self(); pjob.pj_sysjob = 10; (void) strlcpy(pjob.pj_filename, path, SMBD_PJOBLEN); pjob.pj_start_time = time(NULL); pjob.pj_status = 2; pjob.pj_size = sbuf.st_blocks * 512; pjob.pj_page_count = 1; pjob.pj_isspooled = B_TRUE; pjob.pj_jobnum = smbd_cups_jobnum; (void) strlcpy(pjob.pj_jobname, doc_name, SMBD_PJOBLEN); (void) strlcpy(pjob.pj_username, username, SMBD_PJOBLEN); (void) strlcpy(pjob.pj_queuename, SMBD_CUPS_SPOOL_DIR, SMBD_PJOBLEN); cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, pjob.pj_username); if (smb_inet_ntop(ipaddr, clientname, SMB_IPSTRLEN(ipaddr->a_family)) == NULL) { syslog(LOG_INFO, "smbd_spool_copyfile: %s: unknown client", clientname); goto out; } cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-originating-host-name", NULL, clientname); (void) snprintf(new_jobname, SMBD_PJOBLEN, "%s%d", SMBD_FN_PREFIX, pjob.pj_jobnum); cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, new_jobname); (void) snprintf(uri, sizeof (uri) - 1, "/printers/%s", SMBD_PRINTER); response = cups->cupsDoFileRequest(http, request, uri, pjob.pj_filename); if (response != NULL) { if (cups->ippGetStatusCode(response) >= IPP_OK_CONFLICT) { syslog(LOG_ERR, "smbd_spool_copyfile: printer %s: %s", SMBD_PRINTER, cups->ippErrorString(cups->cupsLastError())); } else { atomic_inc_32(&smbd_cups_jobnum); rc = 0; } } else { syslog(LOG_ERR, "smbd_spool_copyfile: unable to print to %s", cups->ippErrorString(cups->cupsLastError())); } if (rc == 0) (void) unlink(pjob.pj_filename); out: if (response) cups->ippDelete(response); if (http) cups->httpClose(http); } int smbd_cups_init(void) { (void) mutex_lock(&smbd_cups_mutex); if (smb_cups.cups_hdl != NULL) { (void) mutex_unlock(&smbd_cups_mutex); return (0); } if ((smb_cups.cups_hdl = dlopen("libcups.so.2", RTLD_NOW)) == NULL) { (void) mutex_unlock(&smbd_cups_mutex); syslog(LOG_DEBUG, "smbd_cups_init: cannot open libcups"); return (ENOENT); } smb_cups.cupsLangDefault = (cups_lang_t *(*)())dlsym(smb_cups.cups_hdl, "cupsLangDefault"); smb_cups.cupsLangEncoding = (const char *(*)(cups_lang_t *)) dlsym(smb_cups.cups_hdl, "cupsLangEncoding"); smb_cups.cupsDoFileRequest = (ipp_t *(*)(http_t *, ipp_t *, const char *, const char *)) dlsym(smb_cups.cups_hdl, "cupsDoFileRequest"); smb_cups.cupsLastError = (ipp_status_t (*)()) dlsym(smb_cups.cups_hdl, "cupsLastError"); smb_cups.cupsLangFree = (void (*)(cups_lang_t *)) dlsym(smb_cups.cups_hdl, "cupsLangFree"); smb_cups.cupsGetDests = (int (*)(cups_dest_t **)) dlsym(smb_cups.cups_hdl, "cupsGetDests"); smb_cups.cupsFreeDests = (void (*)(int, cups_dest_t *)) dlsym(smb_cups.cups_hdl, "cupsFreeDests"); smb_cups.httpClose = (void (*)(http_t *)) dlsym(smb_cups.cups_hdl, "httpClose"); smb_cups.httpConnect = (http_t *(*)(const char *, int)) dlsym(smb_cups.cups_hdl, "httpConnect"); smb_cups.ippNewRequest = (ipp_t *(*)()) dlsym(smb_cups.cups_hdl, "ippNewRequest"); smb_cups.ippDelete = (void (*)())dlsym(smb_cups.cups_hdl, "ippDelete"); smb_cups.ippErrorString = (char *(*)()) dlsym(smb_cups.cups_hdl, "ippErrorString"); smb_cups.ippAddString = (ipp_attribute_t *(*)()) dlsym(smb_cups.cups_hdl, "ippAddString"); smb_cups.ippGetStatusCode = (ipp_status_t (*)(ipp_t *)) dlsym(smb_cups.cups_hdl, "ippGetStatusCode"); if (smb_cups.cupsLangDefault == NULL || smb_cups.cupsLangEncoding == NULL || smb_cups.cupsDoFileRequest == NULL || smb_cups.cupsLastError == NULL || smb_cups.cupsLangFree == NULL || smb_cups.cupsGetDests == NULL || smb_cups.cupsFreeDests == NULL || smb_cups.ippNewRequest == NULL || smb_cups.httpClose == NULL || smb_cups.httpConnect == NULL || smb_cups.ippDelete == NULL || smb_cups.ippErrorString == NULL || smb_cups.ippAddString == NULL || smb_cups.ippGetStatusCode == NULL) { (void) dlclose(smb_cups.cups_hdl); smb_cups.cups_hdl = NULL; (void) mutex_unlock(&smbd_cups_mutex); syslog(LOG_DEBUG, "smbd_cups_init: cannot load libcups"); return (ENOENT); } (void) mutex_unlock(&smbd_cups_mutex); return (0); } void smbd_cups_fini(void) { (void) mutex_lock(&smbd_cups_mutex); if (smb_cups.cups_hdl != NULL) { (void) dlclose(smb_cups.cups_hdl); smb_cups.cups_hdl = NULL; } (void) mutex_unlock(&smbd_cups_mutex); } static smb_cups_ops_t * smbd_cups_ops(void) { if (smb_cups.cups_hdl == NULL) return (NULL); return (&smb_cups); } void smbd_load_printers(void) { pthread_t tid; pthread_attr_t attr; int rc; if (!smb_config_getbool(SMB_CI_PRINT_ENABLE)) return; (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&tid, &attr, smbd_share_printers, NULL); (void) pthread_attr_destroy(&attr); if (rc != 0) syslog(LOG_NOTICE, "unable to load printer shares: %s", strerror(errno)); } /* * All print shares use the path from print$. */ static void * smbd_share_printers(void *arg __unused) { cups_dest_t *dests; cups_dest_t *dest; smb_cups_ops_t *cups; smb_share_t si; uint32_t nerr; int num_dests; int i; if (!smb_config_getbool(SMB_CI_PRINT_ENABLE)) return (NULL); if ((cups = smbd_cups_ops()) == NULL) return (NULL); if (smb_shr_get(SMB_SHARE_PRINT, &si) != NERR_Success) { syslog(LOG_DEBUG, "smbd_share_printers unable to load %s", SMB_SHARE_PRINT); return (NULL); } num_dests = cups->cupsGetDests(&dests); for (i = num_dests, dest = dests; i > 0; i--, dest++) { if (dest->instance != NULL) continue; (void) strlcpy(si.shr_name, dest->name, MAXPATHLEN); smbd_print_share_comment(&si, dest); si.shr_type = STYPE_PRINTQ; nerr = smb_shr_add(&si); if (nerr == NERR_Success || nerr == NERR_DuplicateShare) syslog(LOG_DEBUG, "shared printer: %s", si.shr_name); else syslog(LOG_DEBUG, "smbd_share_printers: unable to add share %s: %u", si.shr_name, nerr); } cups->cupsFreeDests(num_dests, dests); return (NULL); } static void smbd_print_share_comment(smb_share_t *si, cups_dest_t *dest) { cups_option_t *options; char *comment; char *name; char *value; int i; comment = "Print Share"; if ((options = dest->options) == NULL) { (void) strlcpy(si->shr_cmnt, comment, SMB_SHARE_CMNT_MAX); return; } for (i = 0; i < dest->num_options; ++i) { name = options[i].name; value = options[i].value; if (name == NULL || value == NULL || *name == '\0' || *value == '\0') continue; if (strcasecmp(name, "printer-info") == 0) { comment = value; break; } } (void) strlcpy(si->shr_cmnt, comment, SMB_SHARE_CMNT_MAX); } #else /* HAVE_CUPS */ /* * If not HAVE_CUPS, just provide a few "stubs". */ int smbd_cups_init(void) { return (ENOENT); } void smbd_cups_fini(void) { } void smbd_load_printers(void) { } void smbd_spool_init(void) { } void smbd_spool_fini(void) { } void smbd_spool_start(void) { } void smbd_spool_stop(void) { } #endif /* HAVE_CUPS */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include "smbd.h" #define CBUFSIZ 26 /* ctime(3c) */ static const char *pri_name[LOG_DEBUG+1] = { "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" }; static void smb_svc_log(int pri, const char *fmt, va_list ap) { static time_t prev_ts; char fbuf[SMBD_LOG_MSGSIZE]; char cbuf[CBUFSIZ]; char *newfmt; time_t ts; int save_errno = errno; pri &= LOG_PRIMASK; if (smbd.s_debug == 0 && pri == LOG_DEBUG) return; ts = time(NULL); if (prev_ts != ts) { prev_ts = ts; /* NB: cbuf has \n */ (void) fprintf(stdout, "@ %s", ctime_r(&ts, cbuf, sizeof (cbuf))); } newfmt = smb_syslog_fmt_m(fbuf, sizeof (fbuf), fmt, save_errno); flockfile(stdout); (void) fprintf(stdout, "smbd.%s: ", pri_name[pri]); /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) vfprintf(stdout, newfmt, ap); (void) fprintf(stdout, "\n"); funlockfile(stdout); (void) fflush(stdout); } /* * Provide a replacement for libsmb:smb_vsyslog() that prints messages * both to the normal syslog(3C), and to stdout, which ends up in: * /var/svc/log/network-smb-server:default.log * It's much easier to follow debug messages in the service log. */ void smb_vsyslog(int pri, const char *fmt, va_list ap) { va_list tap; va_copy(tap, ap); smb_svc_log(pri, fmt, tap); va_end(tap); vsyslog(pri, fmt, ap); } /* * An override for libsmb:smb_trace(). As the comment there says: * * This function is designed to be used with dtrace, i.e. see: * usr/src/cmd/smbsrv/dtrace/smbd-all.d * * Outside of dtrace, the messages passed to this function usually * lack sufficient context to be useful, so don't log them. * However, if you insist, set debug >= 3 and this will log them. */ void smb_trace(const char *s) { if (smbd.s_debug >= 3) (void) fprintf(stdout, "smbd.trace: %s\n", s); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2018 Nexenta Systems, Inc. All rights reserved. * Copyright 2016 Martin Matuska. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include "smbd.h" /* * This file supports three basic functions that all use the * the zfs_iter_snapshots function to get the snapshot info * from ZFS. If the filesystem is not ZFS, the an error is sent * to the caller (door functions in this case) with the count of * zero in the case of smbd_vss_get_count. Each function * is expecting a path that is the root of the dataset. * The basic idea is to define a structure for the data and * an iterator function that will be called for every snapshot * in the dataset that was opened. The iterator function gets * a zfs_handle_t(that needs to be closed) for the snapshot * and a pointer to the structure of data defined passed to it. * If the iterator function returns a non-zero value, no more * snapshots will be processed. There is no guarantee in the * order in which the snapshots are processed. * * The structure of this file is: * Three structures that are used between the iterator functions * and "main" functions * The 3 "main" functions * Support functions * The 3 iterator functions */ /* * The maximum number of snapshots returned per request. */ #define SMBD_VSS_SNAPSHOT_MAX 725 static void smbd_vss_time2gmttoken(time_t time, char *gmttoken); static int smbd_vss_cmp_time(const void *a, const void *b); static int smbd_vss_iterate_count(zfs_handle_t *zhp, void *data); static int smbd_vss_iterate_get_uint64_date(zfs_handle_t *zhp, void *data); static int smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data); typedef struct smbd_vss_count { int vc_count; } smbd_vss_count_t; /* * gd_count how many @GMT tokens are expected * gd_return_count how many @GMT tokens are being returned * gd_gmt_array array of the @GMT token with max size of gd_count */ typedef struct smbd_vss_get_uint64_date { int gd_count; int gd_return_count; uint64_t *gd_gmt_array; } smbd_vss_get_uint64_date_t; typedef struct smbd_vss_map_gmttoken { time_t mg_snaptime; char *mg_snapname; } smbd_vss_map_gmttoken_t; /* * path - path of the dataset * count - return value of the number of snapshots for the dataset */ int smbd_vss_get_count(const char *path, uint32_t *count) { char dataset[MAXPATHLEN]; libzfs_handle_t *libhd; zfs_handle_t *zfshd; smbd_vss_count_t vss_count; bzero(&vss_count, sizeof (smbd_vss_count_t)); *count = 0; if ((libhd = libzfs_init()) == NULL) return (-1); if (smb_getdataset(libhd, path, dataset, MAXPATHLEN) != 0) { libzfs_fini(libhd); return (-1); } if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) { libzfs_fini(libhd); return (-1); } (void) zfs_iter_snapshots(zfshd, B_FALSE, smbd_vss_iterate_count, (void *)&vss_count); if (vss_count.vc_count > SMBD_VSS_SNAPSHOT_MAX) vss_count.vc_count = SMBD_VSS_SNAPSHOT_MAX; *count = vss_count.vc_count; zfs_close(zfshd); libzfs_fini(libhd); return (0); } /* * path - is the path of the dataset * count - is the maxium number of GMT tokens allowed to be returned * return_count - is how many should be returned * num_gmttokens - how many gmttokens in gmttokenp (0 if error) * gmttokenp - array of @GMT tokens (even if zero, elements still need * to be freed) */ void smbd_vss_get_snapshots(const char *path, uint32_t count, uint32_t *return_count, uint32_t *num_gmttokens, char **gmttokenp) { char dataset[MAXPATHLEN]; libzfs_handle_t *libhd; zfs_handle_t *zfshd; smbd_vss_get_uint64_date_t vss_uint64_date; int i; uint64_t *timep; *return_count = 0; *num_gmttokens = 0; if (count == 0) return; if (count > SMBD_VSS_SNAPSHOT_MAX) count = SMBD_VSS_SNAPSHOT_MAX; vss_uint64_date.gd_count = count; vss_uint64_date.gd_return_count = 0; vss_uint64_date.gd_gmt_array = malloc(count * sizeof (uint64_t)); if (vss_uint64_date.gd_gmt_array == NULL) return; if ((libhd = libzfs_init()) == NULL) { free(vss_uint64_date.gd_gmt_array); return; } if (smb_getdataset(libhd, path, dataset, MAXPATHLEN) != 0) { free(vss_uint64_date.gd_gmt_array); libzfs_fini(libhd); return; } if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) { free(vss_uint64_date.gd_gmt_array); libzfs_fini(libhd); return; } (void) zfs_iter_snapshots(zfshd, B_FALSE, smbd_vss_iterate_get_uint64_date, (void *)&vss_uint64_date); *num_gmttokens = vss_uint64_date.gd_return_count; *return_count = vss_uint64_date.gd_return_count; /* * Sort the list since neither zfs nor the client sorts it. */ qsort((char *)vss_uint64_date.gd_gmt_array, vss_uint64_date.gd_return_count, sizeof (uint64_t), smbd_vss_cmp_time); timep = vss_uint64_date.gd_gmt_array; for (i = 0; i < vss_uint64_date.gd_return_count; i++) { *gmttokenp = malloc(SMB_VSS_GMT_SIZE); if (*gmttokenp) smbd_vss_time2gmttoken(*timep, *gmttokenp); else vss_uint64_date.gd_return_count = 0; timep++; gmttokenp++; } free(vss_uint64_date.gd_gmt_array); zfs_close(zfshd); libzfs_fini(libhd); } static const char smbd_vss_gmttoken_fmt[] = "@GMT-%Y.%m.%d-%H.%M.%S"; /* * path - path of the dataset for the operation * gmttoken - the @GMT token to be looked up * toktime - time_t used if gmttoken == NULL * snapname - the snapshot name to be returned [MAXPATHLEN] * * Here we are going to get the snapshot name from the @GMT token * The snapname returned by ZFS is : @ * So we are going to make sure there is the @ symbol in * the right place and then just return the snapshot name */ int smbd_vss_map_gmttoken(const char *path, char *gmttoken, time_t toktime, char *snapname) { char dataset[MAXPATHLEN]; libzfs_handle_t *libhd; zfs_handle_t *zfshd; smbd_vss_map_gmttoken_t vss_map_gmttoken; char *zsnap; const char *lsnap; struct tm tm; if (gmttoken != NULL && *gmttoken == '@' && strptime(gmttoken, smbd_vss_gmttoken_fmt, &tm) != NULL) { toktime = timegm(&tm); } vss_map_gmttoken.mg_snaptime = toktime; vss_map_gmttoken.mg_snapname = snapname; *snapname = '\0'; if ((libhd = libzfs_init()) == NULL) return (-1); if (smb_getdataset(libhd, path, dataset, MAXPATHLEN) != 0) { libzfs_fini(libhd); return (-1); } if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) { libzfs_fini(libhd); return (-1); } (void) zfs_iter_snapshots(zfshd, B_FALSE, smbd_vss_iterate_map_gmttoken, (void *)&vss_map_gmttoken); /* compare the zfs snapshot name and the local snap name */ zsnap = snapname; lsnap = dataset; while ((*lsnap != '\0') && (*zsnap != '\0') && (*lsnap == *zsnap)) { zsnap++; lsnap++; } /* Now we should be passed the dataset name */ if ((*zsnap == '@') && (*lsnap == '\0')) { zsnap++; (void) strlcpy(snapname, zsnap, MAXPATHLEN); } else { *snapname = '\0'; } zfs_close(zfshd); libzfs_fini(libhd); return (0); } static void smbd_vss_time2gmttoken(time_t time, char *gmttoken) { struct tm t; (void) gmtime_r(&time, &t); (void) strftime(gmttoken, SMB_VSS_GMT_SIZE, smbd_vss_gmttoken_fmt, &t); } static int smbd_vss_cmp_time(const void *a, const void *b) { if (*(uint64_t *)a < *(uint64_t *)b) return (1); if (*(uint64_t *)a == *(uint64_t *)b) return (0); return (-1); } /* * ZFS snapshot iterator to count snapshots. * Note: libzfs expects us to close the handle. * Return 0 to continue iterating or non-zreo to terminate the iteration. */ static int smbd_vss_iterate_count(zfs_handle_t *zhp, void *data) { smbd_vss_count_t *vss_data = data; if (vss_data->vc_count < SMBD_VSS_SNAPSHOT_MAX) { vss_data->vc_count++; zfs_close(zhp); return (0); } zfs_close(zhp); return (-1); } /* * ZFS snapshot iterator to get snapshot creation time. * Note: libzfs expects us to close the handle. * Return 0 to continue iterating or non-zreo to terminate the iteration. */ static int smbd_vss_iterate_get_uint64_date(zfs_handle_t *zhp, void *data) { smbd_vss_get_uint64_date_t *vss_data = data; int count; count = vss_data->gd_return_count; if (count < vss_data->gd_count) { vss_data->gd_gmt_array[count] = zfs_prop_get_int(zhp, ZFS_PROP_CREATION); vss_data->gd_return_count++; zfs_close(zhp); return (0); } zfs_close(zhp); return (-1); } /* * ZFS snapshot iterator to map a snapshot creation time to a token. * Note: libzfs expects us to close the handle. * Return 0 to continue iterating or non-zreo to terminate the iteration. */ static int smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data) { smbd_vss_map_gmttoken_t *vss_data = data; time_t time; time = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION); if (time == vss_data->mg_snaptime) { (void) strlcpy(vss_data->mg_snapname, zfs_get_name(zhp), MAXPATHLEN); /* we found a match, do not process anymore snapshots */ zfs_close(zhp); return (-1); } zfs_close(zhp); return (0); } #!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2016 Hans Rosenfeld # # Scripts that generate IPfilter rules for SMB server . /lib/svc/share/smf_include.sh . /lib/svc/share/ipf_include.sh create_ipf_rules() { FMRI=$1 file=`fmri_to_file $FMRI $IPF_SUFFIX` file6=`fmri_to_file $FMRI $IPF6_SUFFIX` policy=`get_policy ${FMRI}` iana_names="microsoft-ds netbios-ns netbios-dgm netbios-ssn" # # Enforce policy on each port # echo "# $FMRI" >$file echo "# $FMRI" >$file6 for name in $iana_names; do port=`$SERVINFO -p -s $name 2>/dev/null` if [ -z "$port" ]; then continue; fi generate_rules $FMRI $policy "tcp" $port $file generate_rules $FMRI $policy "tcp" $port $file6 _6 generate_rules $FMRI $policy "udp" $port $file generate_rules $FMRI $policy "udp" $port $file6 _6 done } if [ "$1" = "ipfilter" ]; then create_ipf_rules $2 fi exit 0 # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # PROG= smbstat SRCS= smbstat.c include ../../Makefile.cmd include ../../Makefile.ctf OBJS= $(SRCS:%.c=%.o) include ../Makefile.smbsrv.defs LDLIBS += -lkstat -lm -lumem include ../Makefile.smbsrv.targ include ../../Makefile.targ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Nexenta by DDN, Inc. All rights reserved. * Copyright 2023 RackTop Systems, Inc. */ /* * smbstat: Server Message Block File System statistics * * The statistics this CLI displays come from two sources: * * 1) The kernel module 'smbsrv'. * 2) The SMB workers task queue statistics the task queue manager of Solaris * maintains. * * The flow of the code is the following: * * * +----------------+ * | Initialization | * +----------------+ * | * | * v * +--------------------------* * | Take a snapshot the data | <--------+ * +--------------------------+ | * | | * | | * v | * +----------------------+ | * | Process the snapshot | | * +----------------------+ | * | | * | | * v | * +------------------------------------+ | * | Print the result of the processing | | * +------------------------------------+ | * | | * | | * v | * Yes --------------- | * +------------ < interval == 0 ? > | * | --------------- | * | | | * | | No | * | v | * | +------------------------+ | * | | Sleep for the duration | ----------+ * | | of the interval. | * | +------------------------+ * | * +---------------------+ * | * v * * Exit * * There are two sets of snapshots. One set for the smbsrv module and the other * for the task queue (SMB workers). Each set contains 2 snapshots. One is * labeled 'current' the other one 'previous'. Their role changes after each * snapshot. The 'current' becomes 'previous' and vice versa. * The first snapshot taken is compared against the data gathered since the * smbsrv module was loaded. Subsequent snapshots will be compared against the * previous snapshot. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* TEXT_DOMAIN */ #define SMBSTAT_ID_NO_CPU -1 #define SMBSTAT_SNAPSHOT_COUNT 2 /* Must be a power of 2 */ #define SMBSTAT_SNAPSHOT_MASK (SMBSTAT_SNAPSHOT_COUNT - 1) #define SMBSTAT_HELP \ "Usage: smbstat [-acnrtuz] [interval]\n" \ " -c: display counters\n" \ " -t: display throughput\n" \ " -u: display utilization\n" \ " -r: display requests\n" \ " -a: all the requests (supported and unsupported)\n" \ " -z: skip the requests not received\n" \ " -n: display in alphabetic order\n" \ " interval: refresh cycle in seconds\n" #define SMBSRV_COUNTERS_BANNER "\n nbt tcp users trees files pipes\n" #define SMBSRV_COUNTERS_FORMAT "%5d %5d %5d %5d %5d %5d\n" #define SMBSRV_THROUGHPUT_BANNER \ "\nrbytes/s tbytes/s reqs/s reads/s writes/s\n" #define SMBSRV_THROUGHPUT_FORMAT \ "%1.3e %1.3e %1.3e %1.3e %1.3e\n" #define SMBSRV_UTILIZATION_BANNER \ "\n wcnt rcnt wtime rtime" \ " w%% r%% u%% sat usr%% sys%% idle%%\n" #define SMBSRV_UTILIZATION_FORMAT \ "%1.3e %1.3e %1.3e %1.3e %3.0f %3.0f %3.0f %s " \ "%3.0f %3.0f %3.0f\n" #define SMBSRV_REQUESTS_BANNER \ "\n%30s code %% rbytes/s tbytes/s req/s rt-mean" \ " rt-stddev\n" #define SMBSRV_REQUESTS_FORMAT \ "%30s %02X %3.0f %1.3e %1.3e %1.3e %1.3e %1.3e\n" typedef enum { CPU_TICKS_IDLE = 0, CPU_TICKS_USER, CPU_TICKS_KERNEL, CPU_TICKS_SENTINEL } cpu_state_idx_t; typedef struct smbstat_cpu_snapshot { processorid_t cs_id; int cs_state; uint64_t cs_ticks[CPU_TICKS_SENTINEL]; } smbstat_cpu_snapshot_t; typedef struct smbstat_srv_snapshot { hrtime_t ss_snaptime; smbsrv_kstats_t ss_data; } smbstat_srv_snapshot_t; typedef struct smbstat_wrk_snapshot { uint64_t ws_nbacklog; } smbstat_wrk_snapshot_t; typedef struct smbstat_req_info { char ri_name[KSTAT_STRLEN]; int ri_opcode; double ri_pct; double ri_tbs; double ri_rbs; double ri_rqs; double ri_stddev; double ri_mean; } smbstat_req_info_t; typedef struct smbstat_srv_info { double si_hretime; double si_etime; double si_total_nreqs; /* * Counters */ uint32_t si_nbt_sess; /* NBT sessions */ uint32_t si_tcp_sess; /* TCP sessions */ uint32_t si_users; /* Users logged in */ uint32_t si_trees; /* Trees connected */ uint32_t si_files; /* Open files */ uint32_t si_pipes; /* Open pipes */ /* * Throughput of the server */ double si_tbs; /* Bytes transmitted / second */ double si_rbs; /* Bytes received / second */ double si_rqs; /* Requests treated / second */ double si_rds; /* Reads treated / second */ double si_wrs; /* Writes treated / second */ /* * Utilization of the server */ double si_wpct; /* */ double si_rpct; /* */ double si_upct; /* Utilization in % */ double si_avw; /* Average number of requests waiting */ double si_avr; /* Average number of requests running */ double si_wserv; /* Average waiting time */ double si_rserv; /* Average running time */ boolean_t si_sat; double si_ticks[CPU_TICKS_SENTINEL]; /* * Latency & Throughput per request */ smbstat_req_info_t si_reqs1[SMBSRV_KS_NREQS1]; smbstat_req_info_t si_reqs2[SMBSRV_KS_NREQS2]; } smbstat_srv_info_t; static void smbstat_init(void); static void smbstat_fini(void); static void smbstat_kstat_snapshot(void); static void smbstat_kstat_process(void); static void smbstat_kstat_print(void); static void smbstat_print_counters(void); static void smbstat_print_throughput(void); static void smbstat_print_utilization(void); static void smbstat_print_requests(void); static void smbstat_cpu_init(void); static void smbstat_cpu_fini(void); static smbstat_cpu_snapshot_t *smbstat_cpu_current_snapshot(void); static smbstat_cpu_snapshot_t *smbstat_cpu_previous_snapshot(void); static void smbstat_cpu_snapshot(void); static void smbstat_cpu_process(void); static void smbstat_wrk_init(void); static void smbstat_wrk_fini(void); static void smbstat_wrk_snapshot(void); static void smbstat_wrk_process(void); static smbstat_wrk_snapshot_t *smbstat_wrk_current_snapshot(void); static void smbstat_srv_init(void); static void smbstat_srv_fini(void); static void smbstat_srv_snapshot(void); static void smbstat_srv_process(void); static void smbstat_srv_process_counters(smbstat_srv_snapshot_t *); static void smbstat_srv_process_throughput(smbstat_srv_snapshot_t *, smbstat_srv_snapshot_t *); static void smbstat_srv_process_utilization(smbstat_srv_snapshot_t *, smbstat_srv_snapshot_t *); static void smbstat_srv_process_requests(smbstat_srv_snapshot_t *, smbstat_srv_snapshot_t *); static void smbstat_srv_process_one_req(smbstat_req_info_t *, smb_kstat_req_t *, smb_kstat_req_t *, boolean_t); static smbstat_srv_snapshot_t *smbstat_srv_current_snapshot(void); static smbstat_srv_snapshot_t *smbstat_srv_previous_snapshot(void); static void *smbstat_zalloc(size_t); static void smbstat_free(void *, size_t); static void smbstat_fail(int, char *, ...); static void smbstat_snapshot_inc_idx(void); static void smbstat_usage(FILE *, int) __NORETURN; static uint_t smbstat_strtoi(char const *, char *); static double smbstat_hrtime_delta(hrtime_t, hrtime_t); static double smbstat_sub_64(uint64_t, uint64_t); static void smbstat_req_order(void); static double smbstat_zero(double); static void smbstat_termio_init(void); static char *smbstat_cpu_states[CPU_TICKS_SENTINEL] = { "cpu_ticks_idle", "cpu_ticks_user", "cpu_ticks_kernel" }; static boolean_t smbstat_opt_a = B_FALSE; /* all */ static boolean_t smbstat_opt_c = B_FALSE; /* counters */ static boolean_t smbstat_opt_n = B_FALSE; /* by name */ static boolean_t smbstat_opt_u = B_FALSE; /* utilization */ static boolean_t smbstat_opt_t = B_FALSE; /* throughput */ static boolean_t smbstat_opt_r = B_FALSE; /* requests */ static boolean_t smbstat_opt_z = B_FALSE; /* non-zero requests */ static uint_t smbstat_interval = 0; static long smbstat_nrcpus = 0; static kstat_ctl_t *smbstat_ksc = NULL; static kstat_t *smbstat_srv_ksp = NULL; static kstat_t *smbstat_wrk_ksp = NULL; static struct winsize smbstat_ws; static uint16_t smbstat_rows = 0; static int smbstat_snapshot_idx = 0; static smbstat_cpu_snapshot_t *smbstat_cpu_snapshots[SMBSTAT_SNAPSHOT_COUNT]; static smbstat_srv_snapshot_t smbstat_srv_snapshots[SMBSTAT_SNAPSHOT_COUNT]; static smbstat_wrk_snapshot_t smbstat_wrk_snapshots[SMBSTAT_SNAPSHOT_COUNT]; static smbstat_srv_info_t smbstat_srv_info; /* * main */ int main(int argc, char *argv[]) { int c; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if (is_system_labeled()) { (void) fprintf(stderr, gettext("%s: Trusted Extensions not supported.\n"), argv[0]); return (1); } while ((c = getopt(argc, argv, "achnrtuz")) != EOF) { switch (c) { case 'a': smbstat_opt_a = B_TRUE; break; case 'n': smbstat_opt_n = B_TRUE; break; case 'u': smbstat_opt_u = B_TRUE; break; case 'c': smbstat_opt_c = B_TRUE; break; case 'r': smbstat_opt_r = B_TRUE; break; case 't': smbstat_opt_t = B_TRUE; break; case 'z': smbstat_opt_z = B_TRUE; break; case 'h': smbstat_usage(stdout, 0); default: smbstat_usage(stderr, 1); } } if (!smbstat_opt_u && !smbstat_opt_c && !smbstat_opt_r && !smbstat_opt_t) { /* Default options when none is specified. */ smbstat_opt_u = B_TRUE; smbstat_opt_t = B_TRUE; } if (optind < argc) { smbstat_interval = smbstat_strtoi(argv[optind], "invalid count"); optind++; } if ((argc - optind) > 1) smbstat_usage(stderr, 1); (void) atexit(smbstat_fini); smbstat_init(); for (;;) { smbstat_kstat_snapshot(); smbstat_kstat_process(); smbstat_kstat_print(); if (smbstat_interval == 0) break; (void) sleep(smbstat_interval); smbstat_snapshot_inc_idx(); } return (0); } /* * smbstat_init * * Global initialization. */ static void smbstat_init(void) { if ((smbstat_ksc = kstat_open()) == NULL) smbstat_fail(1, gettext("kstat_open(): can't open /dev/kstat")); smbstat_cpu_init(); smbstat_srv_init(); smbstat_wrk_init(); smbstat_req_order(); } /* * smbstat_fini * * Releases the resources smbstat_init() allocated. */ static void smbstat_fini(void) { smbstat_wrk_fini(); smbstat_srv_fini(); smbstat_cpu_fini(); (void) kstat_close(smbstat_ksc); } /* * smbstat_kstat_snapshot * * Takes a snapshot of the data. */ static void smbstat_kstat_snapshot(void) { smbstat_cpu_snapshot(); smbstat_srv_snapshot(); smbstat_wrk_snapshot(); } /* * smbstat_kstat_process */ static void smbstat_kstat_process(void) { smbstat_cpu_process(); smbstat_srv_process(); smbstat_wrk_process(); } /* * smbstat_kstat_print * * Print the data processed. */ static void smbstat_kstat_print(void) { smbstat_termio_init(); smbstat_print_counters(); smbstat_print_throughput(); smbstat_print_utilization(); smbstat_print_requests(); (void) fflush(stdout); } /* * smbstat_print_counters * * Displays the SMB server counters (session, users...). */ static void smbstat_print_counters(void) { if (!smbstat_opt_c) return; if (smbstat_opt_u || smbstat_opt_r || smbstat_opt_t || (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) { (void) printf(SMBSRV_COUNTERS_BANNER); smbstat_rows = 1; } (void) printf(SMBSRV_COUNTERS_FORMAT, smbstat_srv_info.si_nbt_sess, smbstat_srv_info.si_tcp_sess, smbstat_srv_info.si_users, smbstat_srv_info.si_trees, smbstat_srv_info.si_files, smbstat_srv_info.si_pipes); ++smbstat_rows; } /* * smbstat_print_throughput * * Formats the SMB server throughput output. */ static void smbstat_print_throughput(void) { if (!smbstat_opt_t) return; if (smbstat_opt_u || smbstat_opt_r || smbstat_opt_c || (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) { (void) printf(SMBSRV_THROUGHPUT_BANNER); smbstat_rows = 1; } (void) printf(SMBSRV_THROUGHPUT_FORMAT, smbstat_zero(smbstat_srv_info.si_rbs), smbstat_zero(smbstat_srv_info.si_tbs), smbstat_zero(smbstat_srv_info.si_rqs), smbstat_zero(smbstat_srv_info.si_rds), smbstat_zero(smbstat_srv_info.si_wrs)); ++smbstat_rows; } /* * smbstat_print_utilization */ static void smbstat_print_utilization(void) { char *sat; if (!smbstat_opt_u) return; if (smbstat_opt_t || smbstat_opt_r || smbstat_opt_c || (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) { (void) printf(SMBSRV_UTILIZATION_BANNER); smbstat_rows = 1; } if (smbstat_srv_info.si_sat) sat = "yes"; else sat = "no "; (void) printf(SMBSRV_UTILIZATION_FORMAT, smbstat_srv_info.si_avw, smbstat_srv_info.si_avr, smbstat_srv_info.si_wserv, smbstat_srv_info.si_rserv, smbstat_zero(smbstat_srv_info.si_wpct), smbstat_zero(smbstat_srv_info.si_rpct), smbstat_zero(smbstat_srv_info.si_upct), sat, smbstat_srv_info.si_ticks[CPU_TICKS_USER], smbstat_srv_info.si_ticks[CPU_TICKS_KERNEL], smbstat_srv_info.si_ticks[CPU_TICKS_IDLE]); ++smbstat_rows; } /* * smbstat_print_requests */ static void smbstat_print_requests(void) { smbstat_req_info_t *prq; int i; if (!smbstat_opt_r) return; (void) printf(SMBSRV_REQUESTS_BANNER, " "); prq = smbstat_srv_info.si_reqs1; for (i = 0; i < SMBSRV_KS_NREQS1; i++) { if (!smbstat_opt_a && strncmp(prq[i].ri_name, "Invalid", sizeof ("Invalid")) == 0) continue; if (!smbstat_opt_z || (prq[i].ri_pct != 0)) { (void) printf(SMBSRV_REQUESTS_FORMAT, prq[i].ri_name, prq[i].ri_opcode, smbstat_zero(prq[i].ri_pct), smbstat_zero(prq[i].ri_rbs), smbstat_zero(prq[i].ri_tbs), smbstat_zero(prq[i].ri_rqs), prq[i].ri_mean, prq[i].ri_stddev); } } prq = smbstat_srv_info.si_reqs2; for (i = 0; i < SMBSRV_KS_NREQS2; i++) { if (!smbstat_opt_a && i == SMB2_INVALID_CMD) continue; if (!smbstat_opt_z || (prq[i].ri_pct != 0)) { (void) printf(SMBSRV_REQUESTS_FORMAT, prq[i].ri_name, prq[i].ri_opcode, smbstat_zero(prq[i].ri_pct), smbstat_zero(prq[i].ri_rbs), smbstat_zero(prq[i].ri_tbs), smbstat_zero(prq[i].ri_rqs), prq[i].ri_mean, prq[i].ri_stddev); } } } /* * smbstat_cpu_init */ static void smbstat_cpu_init(void) { size_t size; int i; smbstat_nrcpus = sysconf(_SC_CPUID_MAX) + 1; size = smbstat_nrcpus * sizeof (smbstat_cpu_snapshot_t); for (i = 0; i < SMBSTAT_SNAPSHOT_COUNT; i++) smbstat_cpu_snapshots[i] = smbstat_zalloc(size); } /* * smbstat_cpu_fini */ static void smbstat_cpu_fini(void) { size_t size; int i; size = smbstat_nrcpus * sizeof (smbstat_cpu_snapshot_t); for (i = 0; i < SMBSTAT_SNAPSHOT_COUNT; i++) smbstat_free(smbstat_cpu_snapshots[i], size); } /* * smbstat_cpu_current_snapshot */ static smbstat_cpu_snapshot_t * smbstat_cpu_current_snapshot(void) { return (smbstat_cpu_snapshots[smbstat_snapshot_idx]); } /* * smbstat_cpu_previous_snapshot */ static smbstat_cpu_snapshot_t * smbstat_cpu_previous_snapshot(void) { int idx; idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK; return (smbstat_cpu_snapshots[idx]); } /* * smbstat_cpu_snapshot */ static void smbstat_cpu_snapshot(void) { kstat_t *ksp; kstat_named_t *ksn; smbstat_cpu_snapshot_t *curr; long i; int j; curr = smbstat_cpu_current_snapshot(); for (i = 0; i < smbstat_nrcpus; i++, curr++) { curr->cs_id = SMBSTAT_ID_NO_CPU; curr->cs_state = p_online(i, P_STATUS); /* If no valid CPU is present, move on to the next one */ if (curr->cs_state != P_ONLINE && curr->cs_state != P_NOINTR) continue; curr->cs_id = i; ksp = kstat_lookup(smbstat_ksc, "cpu", i, "sys"); if (ksp == NULL) smbstat_fail(1, gettext("kstat_lookup('cpu sys %d') failed"), i); if (kstat_read(smbstat_ksc, ksp, NULL) == -1) smbstat_fail(1, gettext("kstat_read('cpu sys %d') failed"), i); for (j = 0; j < CPU_TICKS_SENTINEL; j++) { ksn = kstat_data_lookup(ksp, smbstat_cpu_states[j]); if (ksn == NULL) smbstat_fail(1, gettext("kstat_data_lookup('%s') failed"), smbstat_cpu_states[j]); curr->cs_ticks[j] = ksn->value.ui64; } } } /* * smbstat_cpu_process */ static void smbstat_cpu_process(void) { smbstat_cpu_snapshot_t *curr, *prev; double total_ticks; double agg_ticks[CPU_TICKS_SENTINEL]; int i, j; curr = smbstat_cpu_current_snapshot(); prev = smbstat_cpu_previous_snapshot(); bzero(agg_ticks, sizeof (agg_ticks)); total_ticks = 0; for (i = 0; i < smbstat_nrcpus; i++, curr++, prev++) { for (j = 0; j < CPU_TICKS_SENTINEL; j++) { agg_ticks[j] += smbstat_sub_64(curr->cs_ticks[j], prev->cs_ticks[j]); total_ticks += smbstat_sub_64(curr->cs_ticks[j], prev->cs_ticks[j]); } } for (j = 0; j < CPU_TICKS_SENTINEL; j++) smbstat_srv_info.si_ticks[j] = (agg_ticks[j] * 100.0) / total_ticks; } /* * smbstat_wrk_init */ static void smbstat_wrk_init(void) { smbstat_wrk_ksp = kstat_lookup(smbstat_ksc, "unix", -1, SMBSRV_KSTAT_WORKERS); if (smbstat_wrk_ksp == NULL) smbstat_fail(1, gettext("cannot retrieve smbsrv workers kstat\n")); } static void smbstat_wrk_fini(void) { smbstat_wrk_ksp = NULL; } /* * smbstat_wrk_snapshot */ static void smbstat_wrk_snapshot(void) { smbstat_wrk_snapshot_t *curr; kstat_named_t *kn; curr = smbstat_wrk_current_snapshot(); if (kstat_read(smbstat_ksc, smbstat_wrk_ksp, NULL) == -1) smbstat_fail(1, gettext("kstat_read('%s') failed"), smbstat_wrk_ksp->ks_name); kn = kstat_data_lookup(smbstat_wrk_ksp, "nbacklog"); if ((kn == NULL) || (kn->data_type != KSTAT_DATA_UINT64)) smbstat_fail(1, gettext("kstat_read('%s') failed"), "nbacklog"); curr->ws_nbacklog = kn->value.ui64; } /* * smbstat_wrk_process */ static void smbstat_wrk_process(void) { smbstat_wrk_snapshot_t *curr; curr = smbstat_wrk_current_snapshot(); if (curr->ws_nbacklog != 0) smbstat_srv_info.si_sat = B_TRUE; else smbstat_srv_info.si_sat = B_FALSE; } /* * smbstat_wrk_current_snapshot */ static smbstat_wrk_snapshot_t * smbstat_wrk_current_snapshot(void) { return (&smbstat_wrk_snapshots[smbstat_snapshot_idx]); } /* * smbstat_srv_init */ static void smbstat_srv_init(void) { smbstat_srv_ksp = kstat_lookup(smbstat_ksc, SMBSRV_KSTAT_MODULE, getzoneid(), SMBSRV_KSTAT_STATISTICS); if (smbstat_srv_ksp == NULL) smbstat_fail(1, gettext("cannot retrieve smbsrv kstat\n")); } /* * smbstat_srv_fini */ static void smbstat_srv_fini(void) { smbstat_srv_ksp = NULL; } /* * smbstat_srv_snapshot * * Take a snapshot of the smbsrv module statistics. */ static void smbstat_srv_snapshot(void) { smbstat_srv_snapshot_t *curr; curr = smbstat_srv_current_snapshot(); if ((kstat_read(smbstat_ksc, smbstat_srv_ksp, NULL) == -1) || (smbstat_srv_ksp->ks_data_size != sizeof (curr->ss_data))) smbstat_fail(1, gettext("kstat_read('%s') failed"), smbstat_srv_ksp->ks_name); curr->ss_snaptime = smbstat_srv_ksp->ks_snaptime; bcopy(smbstat_srv_ksp->ks_data, &curr->ss_data, sizeof (curr->ss_data)); } /* * smbstat_srv_process * * Processes the snapshot data. */ static void smbstat_srv_process(void) { smbstat_srv_snapshot_t *curr, *prev; curr = smbstat_srv_current_snapshot(); prev = smbstat_srv_previous_snapshot(); if (prev->ss_snaptime == 0) smbstat_srv_info.si_hretime = smbstat_hrtime_delta(curr->ss_data.ks_start_time, curr->ss_snaptime); else smbstat_srv_info.si_hretime = smbstat_hrtime_delta(prev->ss_snaptime, curr->ss_snaptime); smbstat_srv_info.si_etime = smbstat_srv_info.si_hretime / NANOSEC; smbstat_srv_info.si_total_nreqs = smbstat_sub_64(curr->ss_data.ks_nreq, prev->ss_data.ks_nreq); if (smbstat_opt_c) smbstat_srv_process_counters(curr); if (smbstat_opt_t) smbstat_srv_process_throughput(curr, prev); if (smbstat_opt_u) smbstat_srv_process_utilization(curr, prev); if (smbstat_opt_r) smbstat_srv_process_requests(curr, prev); } /* * smbstat_srv_process_counters */ static void smbstat_srv_process_counters(smbstat_srv_snapshot_t *curr) { smbstat_srv_info.si_nbt_sess = curr->ss_data.ks_nbt_sess; smbstat_srv_info.si_tcp_sess = curr->ss_data.ks_tcp_sess; smbstat_srv_info.si_users = curr->ss_data.ks_users; smbstat_srv_info.si_trees = curr->ss_data.ks_trees; smbstat_srv_info.si_files = curr->ss_data.ks_files; smbstat_srv_info.si_pipes = curr->ss_data.ks_pipes; } /* * smbstat_srv_process_throughput * * Processes the data relative to the throughput of the smbsrv module and * stores the results in the structure smbstat_srv_info. */ static void smbstat_srv_process_throughput( smbstat_srv_snapshot_t *curr, smbstat_srv_snapshot_t *prev) { smbstat_srv_info.si_tbs = smbstat_sub_64(curr->ss_data.ks_txb, prev->ss_data.ks_txb); smbstat_srv_info.si_tbs /= smbstat_srv_info.si_etime; smbstat_srv_info.si_rbs = smbstat_sub_64(curr->ss_data.ks_rxb, prev->ss_data.ks_rxb); smbstat_srv_info.si_rbs /= smbstat_srv_info.si_etime; smbstat_srv_info.si_rqs = smbstat_srv_info.si_total_nreqs; smbstat_srv_info.si_rqs /= smbstat_srv_info.si_etime; smbstat_srv_info.si_rds = smbstat_sub_64( curr->ss_data.ks_reqs1[SMB_COM_READ].kr_nreq, prev->ss_data.ks_reqs1[SMB_COM_READ].kr_nreq); smbstat_srv_info.si_rds += smbstat_sub_64( curr->ss_data.ks_reqs1[SMB_COM_LOCK_AND_READ].kr_nreq, prev->ss_data.ks_reqs1[SMB_COM_LOCK_AND_READ].kr_nreq); smbstat_srv_info.si_rds += smbstat_sub_64( curr->ss_data.ks_reqs1[SMB_COM_READ_RAW].kr_nreq, prev->ss_data.ks_reqs1[SMB_COM_READ_RAW].kr_nreq); smbstat_srv_info.si_rds += smbstat_sub_64( curr->ss_data.ks_reqs1[SMB_COM_READ_ANDX].kr_nreq, prev->ss_data.ks_reqs1[SMB_COM_READ_ANDX].kr_nreq); smbstat_srv_info.si_rds += smbstat_sub_64( curr->ss_data.ks_reqs2[SMB2_READ].kr_nreq, prev->ss_data.ks_reqs2[SMB2_READ].kr_nreq); smbstat_srv_info.si_rds /= smbstat_srv_info.si_etime; smbstat_srv_info.si_wrs = smbstat_sub_64( curr->ss_data.ks_reqs1[SMB_COM_WRITE].kr_nreq, prev->ss_data.ks_reqs1[SMB_COM_WRITE].kr_nreq); smbstat_srv_info.si_wrs += smbstat_sub_64( curr->ss_data.ks_reqs1[SMB_COM_WRITE_AND_UNLOCK].kr_nreq, prev->ss_data.ks_reqs1[SMB_COM_WRITE_AND_UNLOCK].kr_nreq); smbstat_srv_info.si_wrs += smbstat_sub_64( curr->ss_data.ks_reqs1[SMB_COM_WRITE_RAW].kr_nreq, prev->ss_data.ks_reqs1[SMB_COM_WRITE_RAW].kr_nreq); smbstat_srv_info.si_wrs += smbstat_sub_64( curr->ss_data.ks_reqs1[SMB_COM_WRITE_AND_CLOSE].kr_nreq, prev->ss_data.ks_reqs1[SMB_COM_WRITE_AND_CLOSE].kr_nreq); smbstat_srv_info.si_wrs += smbstat_sub_64( curr->ss_data.ks_reqs1[SMB_COM_WRITE_ANDX].kr_nreq, prev->ss_data.ks_reqs1[SMB_COM_WRITE_ANDX].kr_nreq); smbstat_srv_info.si_wrs += smbstat_sub_64( curr->ss_data.ks_reqs2[SMB2_WRITE].kr_nreq, prev->ss_data.ks_reqs2[SMB2_WRITE].kr_nreq); smbstat_srv_info.si_wrs /= smbstat_srv_info.si_etime; } /* * smbstat_srv_process_utilization * * Processes the data relative to the utilization of the smbsrv module and * stores the results in the structure smbstat_srv_info. */ static void smbstat_srv_process_utilization( smbstat_srv_snapshot_t *curr, smbstat_srv_snapshot_t *prev) { double tw_delta, tr_delta; double w_delta, r_delta; double tps, rqs; w_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_wlentime, curr->ss_data.ks_utilization.ku_wlentime); r_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_rlentime, curr->ss_data.ks_utilization.ku_rlentime); tw_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_wtime, curr->ss_data.ks_utilization.ku_wtime); tr_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_rtime, curr->ss_data.ks_utilization.ku_rtime); rqs = smbstat_srv_info.si_total_nreqs / smbstat_srv_info.si_etime; /* Average number of requests waiting */ if (w_delta != 0) smbstat_srv_info.si_avw = w_delta / smbstat_srv_info.si_hretime; else smbstat_srv_info.si_avw = 0.0; /* Average number of request running */ if (r_delta != 0) smbstat_srv_info.si_avr = r_delta / smbstat_srv_info.si_hretime; else smbstat_srv_info.si_avr = 0.0; /* Utilization */ smbstat_srv_info.si_upct = (smbstat_srv_info.si_avr / curr->ss_data.ks_maxreqs) * 100; /* Average wait service time in milliseconds */ smbstat_srv_info.si_rserv = 0.0; smbstat_srv_info.si_wserv = 0.0; if (rqs > 0.0 && (smbstat_srv_info.si_avw != 0.0 || smbstat_srv_info.si_avr != 0.0)) { tps = 1 / rqs; if (smbstat_srv_info.si_avw != 0.0) smbstat_srv_info.si_wserv = smbstat_srv_info.si_avw * tps; if (smbstat_srv_info.si_avr != 0.0) smbstat_srv_info.si_rserv = smbstat_srv_info.si_avr * tps; } /* % of time there is a transaction waiting for service */ if (tw_delta != 0) { smbstat_srv_info.si_wpct = tw_delta; smbstat_srv_info.si_wpct /= smbstat_srv_info.si_hretime; smbstat_srv_info.si_wpct *= 100.0; } else { smbstat_srv_info.si_wpct = 0.0; } /* % of time there is a transaction running */ if (tr_delta != 0) { smbstat_srv_info.si_rpct = tr_delta; smbstat_srv_info.si_rpct /= smbstat_srv_info.si_hretime; smbstat_srv_info.si_rpct *= 100.0; } else { smbstat_srv_info.si_rpct = 0.0; } } /* * smbstat_srv_process_requests * * Processes the data relative to the SMB requests and stores the results in * the structure smbstat_srv_info. */ static void smbstat_srv_process_requests( smbstat_srv_snapshot_t *curr, smbstat_srv_snapshot_t *prev) { smbstat_req_info_t *info; smb_kstat_req_t *curr_req; smb_kstat_req_t *prev_req; int i, idx; boolean_t firstcall = (prev->ss_snaptime == 0); for (i = 0; i < SMBSRV_KS_NREQS1; i++) { info = &smbstat_srv_info.si_reqs1[i]; idx = info->ri_opcode; if (idx >= SMBSRV_KS_NREQS1) continue; curr_req = &curr->ss_data.ks_reqs1[idx]; prev_req = &prev->ss_data.ks_reqs1[idx]; smbstat_srv_process_one_req( info, curr_req, prev_req, firstcall); } for (i = 0; i < SMBSRV_KS_NREQS2; i++) { info = &smbstat_srv_info.si_reqs2[i]; idx = info->ri_opcode; if (idx >= SMBSRV_KS_NREQS2) continue; curr_req = &curr->ss_data.ks_reqs2[idx]; prev_req = &prev->ss_data.ks_reqs2[idx]; smbstat_srv_process_one_req( info, curr_req, prev_req, firstcall); } } static void smbstat_srv_process_one_req( smbstat_req_info_t *info, smb_kstat_req_t *curr_req, smb_kstat_req_t *prev_req, boolean_t firstcall) { double nrqs; nrqs = smbstat_sub_64(curr_req->kr_nreq, prev_req->kr_nreq); info->ri_rqs = nrqs / smbstat_srv_info.si_etime; info->ri_rbs = smbstat_sub_64( curr_req->kr_rxb, prev_req->kr_rxb) / smbstat_srv_info.si_etime; info->ri_tbs = smbstat_sub_64( curr_req->kr_txb, prev_req->kr_txb) / smbstat_srv_info.si_etime; info->ri_pct = nrqs * 100; if (smbstat_srv_info.si_total_nreqs > 0) info->ri_pct /= smbstat_srv_info.si_total_nreqs; if (firstcall) { /* First time. Take the aggregate */ info->ri_stddev = curr_req->kr_a_stddev; info->ri_mean = curr_req->kr_a_mean; } else { /* Take the differential */ info->ri_stddev = curr_req->kr_d_stddev; info->ri_mean = curr_req->kr_d_mean; } if (nrqs > 0) { info->ri_stddev /= nrqs; info->ri_stddev = sqrt(info->ri_stddev); } else { info->ri_stddev = 0; } info->ri_stddev /= NANOSEC; info->ri_mean /= NANOSEC; } /* * smbstat_srv_current_snapshot * * Returns the current snapshot. */ static smbstat_srv_snapshot_t * smbstat_srv_current_snapshot(void) { return (&smbstat_srv_snapshots[smbstat_snapshot_idx]); } /* * smbstat_srv_previous_snapshot * * Returns the previous snapshot. */ static smbstat_srv_snapshot_t * smbstat_srv_previous_snapshot(void) { int idx; idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK; return (&smbstat_srv_snapshots[idx]); } /* * smbstat_usage * * Prints out a help message. */ static void smbstat_usage(FILE *fd, int exit_code) { (void) fprintf(fd, gettext(SMBSTAT_HELP)); exit(exit_code); } /* * smbstat_fail * * Prints out to stderr an error message and exits the process. */ static void smbstat_fail(int do_perror, char *message, ...) { va_list args; va_start(args, message); (void) fprintf(stderr, gettext("smbstat: ")); /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) vfprintf(stderr, message, args); va_end(args); if (do_perror) (void) fprintf(stderr, ": %s", strerror(errno)); (void) fprintf(stderr, "\n"); exit(1); } /* * smbstat_sub_64 * * Substract 2 uint64_t and returns a double. */ static double smbstat_sub_64(uint64_t a, uint64_t b) { return ((double)(a - b)); } /* * smbstat_zero * * Returns zero if the value passed in is less than 1. */ static double smbstat_zero(double value) { if (value < 1) value = 0; return (value); } /* * smbstat_strtoi * * Converts a string representing an integer value into its binary value. * If the conversion fails this routine exits the process. */ static uint_t smbstat_strtoi(char const *val, char *errmsg) { char *end; long tmp; errno = 0; tmp = strtol(val, &end, 10); if (*end != '\0' || errno) smbstat_fail(1, "%s %s", errmsg, val); return ((uint_t)tmp); } /* * smbstat_termio_init * * Determines the size of the terminal associated with the process. */ static void smbstat_termio_init(void) { char *envp; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &smbstat_ws) != -1) { if (smbstat_ws.ws_row == 0) { envp = getenv("LINES"); if (envp != NULL) smbstat_ws.ws_row = atoi(envp); } if (smbstat_ws.ws_col == 0) { envp = getenv("COLUMNS"); if (envp != NULL) smbstat_ws.ws_row = atoi(envp); } } if (smbstat_ws.ws_col == 0) smbstat_ws.ws_col = 80; if (smbstat_ws.ws_row == 0) smbstat_ws.ws_row = 25; } /* * smbstat_snapshot_idx_inc * * Increments the snapshot index. */ static void smbstat_snapshot_inc_idx(void) { smbstat_snapshot_idx++; smbstat_snapshot_idx &= SMBSTAT_SNAPSHOT_MASK; } /* * smbstat_req_cmp_name * * Call back function passed to qsort() when the list of requests must be sorted * by name. */ static int smbstat_req_cmp_name(const void *obj1, const void *obj2) { return (strncasecmp( ((smbstat_req_info_t *)obj1)->ri_name, ((smbstat_req_info_t *)obj2)->ri_name, sizeof (((smbstat_req_info_t *)obj2)->ri_name))); } /* * smbstat_req_order * * Snapshots the smbsrv module statistics once to get the name of the requests. * The request list is smbstat_srv_info is then sorted by name or by code * depending on the boolean smbstat_opt_a. * The function should be called once during initialization. */ static void smbstat_req_order(void) { smbstat_srv_snapshot_t *ss; smbstat_req_info_t *info; smb_kstat_req_t *reqs; int i; smbstat_srv_snapshot(); ss = smbstat_srv_current_snapshot(); reqs = ss->ss_data.ks_reqs1; info = smbstat_srv_info.si_reqs1; for (i = 0; i < SMBSRV_KS_NREQS1; i++) { (void) strlcpy(info[i].ri_name, reqs[i].kr_name, sizeof (reqs[i].kr_name)); info[i].ri_opcode = i; } if (smbstat_opt_n) qsort(info, SMBSRV_KS_NREQS1, sizeof (smbstat_req_info_t), smbstat_req_cmp_name); reqs = ss->ss_data.ks_reqs2; info = smbstat_srv_info.si_reqs2; for (i = 0; i < SMBSRV_KS_NREQS2; i++) { (void) strlcpy(info[i].ri_name, reqs[i].kr_name, sizeof (reqs[i].kr_name)); info[i].ri_opcode = i; } if (smbstat_opt_n) qsort(info, SMBSRV_KS_NREQS2, sizeof (smbstat_req_info_t), smbstat_req_cmp_name); } /* * Return the number of ticks delta between two hrtime_t * values. Attempt to cater for various kinds of overflow * in hrtime_t - no matter how improbable. */ static double smbstat_hrtime_delta(hrtime_t old, hrtime_t new) { uint64_t del; if ((new >= old) && (old >= 0L)) return ((double)(new - old)); /* * We've overflowed the positive portion of an hrtime_t. */ if (new < 0L) { /* * The new value is negative. Handle the case where the old * value is positive or negative. */ uint64_t n1; uint64_t o1; n1 = -new; if (old > 0L) return ((double)(n1 - old)); o1 = -old; del = n1 - o1; return ((double)del); } /* * Either we've just gone from being negative to positive *or* the last * entry was positive and the new entry is also positive but *less* than * the old entry. This implies we waited quite a few days on a very fast * system between displays. */ if (old < 0L) { uint64_t o2; o2 = -old; del = UINT64_MAX - o2; } else { del = UINT64_MAX - old; } del += new; return ((double)del); } static void * smbstat_zalloc(size_t size) { void *ptr; ptr = umem_zalloc(size, UMEM_DEFAULT); if (ptr == NULL) smbstat_fail(1, gettext("out of memory")); return (ptr); } static void smbstat_free(void *ptr, size_t size) { umem_free(ptr, size); } # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2021 Tintri by DDN, Inc. All rights reserved. # # gdb script (it allows python :) python import os gdb.execute('file' + os.environ['ROOT'] + '/usr/lib/smbsrv/test-decrypt') gdb.execute('set env ' + 'LD_LIBRARY_PATH ' + \ os.environ['ROOT'] + '/lib:' + \ os.environ['ROOT'] + '/usr/lib') gdb.execute('set env ' + 'PATH_PKCS11_CONF ' + \ os.environ['ROOT'] + '/etc/crypto/pkcs11.conf') end # break C_DecryptUpdate # break C_DecryptFinal # break soft_aes_decrypt_update # break aes_decrypt_contiguous_blocks # break ccm_mode_decrypt_contiguous_blocks # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2018-2021 Tintri by DDN, Inc. All rights reserved. # PROG= test-encrypt test-decrypt LIBFAKEKERN=../../../lib/libfakekernel/common LIBFKSMBSRV=../../../lib/smbsrv/libfksmbsrv/common SMBSRV_SRCDIR=../../../uts/common/fs/smbsrv SMBSRV_CMNDIR=../../../common/smbsrv include ../../Makefile.cmd include ../../Makefile.ctf ZGUIDANCE= # Note: need our sys includes _before_ ENVCPPFLAGS, proto etc. # The real cmn_err.h has conflicts w stdio printf (fix?) CPPFLAGS.first += -I. CPPFLAGS.first += -I$(LIBFAKEKERN) INCS += -I../../../uts/common INCS += -I../../../uts/common/smbsrv INCS += -I../../../common/smbsrv CSTD= $(CSTD_GNU99) C99LMODE= -Xc99=%all CFLAGS += $(CCVERBOSE) CFLAGS64 += $(CCVERBOSE) CERRWARN += -Wno-parentheses CPPFLAGS += -D_REENTRANT CPPFLAGS += -DTESTJIG CPPFLAGS += -Dsyslog=smb_syslog CPPFLAGS += -D_LARGEFILE64_SOURCE=1 CPPFLAGS += -D_FAKE_KERNEL # Always debug here CPPFLAGS += -DDEBUG CPPFLAGS += $(INCS) LDFLAGS += $(ZNOLAZYLOAD) LDLIBS += -lfakekernel LDLIBS += -lpkcs11 ROOTSMBDDIR = $(ROOTLIB)/smbsrv ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%) OBJS= .KEEP_STATE: all: $(PROG) install: all $(ROOTSMBDFILE) clean: -$(RM) *.o lint: TEOBJS= test_encrypt.o utils.o fksmb_encrypt_pkcs.o test-encrypt : OBJS = $(TEOBJS) test-encrypt : $(TEOBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) TDOBJS= test_decrypt.o utils.o fksmb_encrypt_pkcs.o test-decrypt : OBJS = $(TDOBJS) test-decrypt : $(TDOBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) include ../../Makefile.targ # LIBFKSMBSRV %.o: $(LIBFKSMBSRV)/%.c $(CC) $(CFLAGS) $(CPPFLAGS) \ -I../../../uts/common/smbsrv \ -I../../../common/smbsrv -c $< $(POST_PROCESS_O) $(ROOTSMBDDIR)/%: % $(INS.file) .KEEP_STATE: # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2021 Tintri by DDN, Inc. All rights reserved. # This directory contains a simple test of the encrypt/decrypt interfaces described by smbsrv/smb_kcrypt.h To build the tests: make install To run the tests: (on the build system) ./Run.sh For debugging, start by uncommenting the dtrace lines in the Run.sh script, and/or run with gdb (see .gdbrc) If you need to debug in the crypto provider libraries (or even just look with gdb) it's easiest to run with a customized pkcs11.conf See how the Run.sh file sets the environment for that. Your custom pkcs11.conf file should look like this: (Substitute your own WS root in there.) # Start SUNWcsr metaslot:metaslot_status=enabled;metaslot_auto_key_migrate=enabled;metaslot_token=Sun Software PKCS#11 softtoken;metaslot_slot=Sun Crypto Softtoken /tank/ws2/il-fc-smb/proto/root_i386/usr/lib/security/$ISA/pkcs11_kernel.so /tank/ws2/il-fc-smb/proto/root_i386/usr/lib/security/$ISA/pkcs11_softtoken.so /tank/ws2/il-fc-smb/proto/root_i386/usr/lib/security/$ISA/pkcs11_tpm.so # End SUNWcsr #!/bin/sh # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2018-2021 Tintri by DDN, Inc. All rights reserved. # # Helper program to run test-crypt (unit test program) # using binaries from the proto area. [ -n "$CODEMGR_WS" ] || { echo "Need a buildenv to set CODEMGR_WS=..." exit 1; } ROOT=${CODEMGR_WS}/proto/root_i386 LD_LIBRARY_PATH=$ROOT/usr/lib:$ROOT/lib export LD_LIBRARY_PATH export UMEM_DEBUG=default PATH_PKCS11_CONF="$ROOT/etc/crypto/pkcs11.conf" export PATH_PKCS11_CONF # ldd $ROOT/usr/lib/smbsrv/test-encrypt set -x # sudo -s dtrace -s Watch.d -o test-encrypt.dto -c \ $ROOT/usr/lib/smbsrv/test-encrypt # sudo -s dtrace -s Watch.d -o test-decrypt.dto -c \ $ROOT/usr/lib/smbsrv/test-decrypt #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017-2021 Tintri by DDN, Inc. All rights reserved. */ /* * Trace almost everything */ pid$target:test-*::entry, pid$target:libpkcs11.so.1::entry, pid$target:libsoftcrypto.so.1::entry, pid$target:pkcs11_softtoken.so.1::entry { printf("\t0x%x", arg0); printf("\t0x%x", arg1); printf("\t0x%x", arg2); printf("\t0x%x", arg3); printf("\t0x%x", arg4); printf("\t0x%x", arg5); } pid$target:test-*::return, pid$target:libpkcs11.so.1::return, pid$target:libsoftcrypto.so.1::return, pid$target:pkcs11_softtoken.so.1::return { printf("\t0x%x", arg1); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ #ifndef _TEST_DATA_H #define _TEST_DATA_H /* * Test data for the encrypt/decrypt tests. */ #ifdef __cplusplus extern "C" { #endif /* * Don't use static data for a "nonce" in real life! */ char authdata[] = "Authentication16"; char keydata[] = "The Key Data 16"; char nonce[] = "Noncedata11"; const char clear_data_ref[] = "\"The Road Not Taken\" by Robert Frost\n" "\n" "Two roads diverged in a yellow wood,\n" "And sorry I could not travel both\n" "And be one traveler, long I stood\n" "And looked down one as far as I could\n" "To where it bent in the undergrowth;\n" "Then took the other, as just as fair,\n" "And having perhaps the better claim\n" "Because it was grassy and wanted wear,\n" "Though as for that the passing there\n" "Had worn them really about the same,\n" "And both that morning equally lay\n" "In leaves no step had trodden black.\n" "Oh, I kept the first for another day!\n" "Yet knowing how way leads on to way\n" "I doubted if I should ever come back.\n" "I shall be telling this with a sigh\n" "Somewhere ages and ages hence:\n" "Two roads diverged in a wood, and I,\n" "I took the one less traveled by,\n" "And that has made all the difference.\n" "\n"; size_t clear_data_len = sizeof (clear_data_ref); #define CLEAR_DATA_LEN 765 #define CIPHER_DATA_LEN (CLEAR_DATA_LEN + 16) // Cipher for the above, using CKM_AES_CCM const uint8_t cipher_data_ccm[CIPHER_DATA_LEN] __nonstring = "\x60\xcc\xb4\x54\x32\xa4\x45\x7f\x90\x4d\x27\x54\x9d\x0f\x06\x92" "\xff\x6f\x90\xe3\x5c\xcf\xdd\xed\x52\x51\x6a\x5f\x0d\x84\xe6\x4d" "\x3b\xae\x52\xe4\xdc\x20\xdd\xb6\xd5\x24\xc7\x7c\xbe\x08\xb7\xd8" "\x5c\x32\x95\x72\x9a\x7d\xd4\x02\xbb\xed\x8a\x0a\xbc\xc1\x97\x59" "\x44\x65\xab\x7a\xd7\xe9\x7d\x7e\x8f\x77\x8c\xc7\x62\x42\xb1\x42" "\xcd\x9e\x6e\x96\x1f\x57\x01\xba\x7d\x2b\xf5\x55\x0e\x61\x85\xeb" "\xd2\x33\x6a\xc2\xac\x42\xf7\xa4\x4d\xf4\xf5\xb3\x59\xe6\x9b\x7c" "\x37\x01\x71\x24\x92\x1a\x14\xf7\x7f\x37\x92\x0f\x34\x0c\xbc\xf8" "\x02\x95\x88\xa5\xed\xc7\x1c\x6c\x68\xe7\x10\x43\xde\x1e\x35\x77" "\x1c\xae\x4a\x12\xdf\x3d\x88\x7a\xf8\x59\xb9\xfc\x54\x2a\x94\x3b" "\x59\xcf\xd7\x85\x35\x37\x35\x43\xb5\x3e\xb6\xa2\xc4\x27\x7e\x92" "\xd5\x68\x8d\xe8\xfb\x13\xa7\x65\xa1\x81\xf3\x18\xc4\xe7\xee\x3a" "\x19\x3c\xd6\xde\xd2\x8e\xfd\xce\x97\xb4\xe9\xc0\x7d\x64\x5d\x15" "\x1a\x50\x43\x66\xcb\x62\xc0\xa2\x77\x33\x1e\x59\xea\x6c\x65\x9e" "\x78\xda\x9d\xb4\x35\x78\x06\x5d\xef\x7f\x78\x13\xa5\xa0\x94\x23" "\x4d\x95\x07\x46\xc1\x42\x07\xa5\x2f\xb3\xc2\xe0\x80\x83\x35\x93" "\xaf\x40\x48\x9f\x86\xf3\x7d\x96\xca\x4a\xba\x84\x9b\xec\x9e\x02" "\xba\x89\x6e\x5e\x9b\x7b\x6b\x38\xf7\xf3\xf7\xd3\xeb\x33\xe6\x8c" "\x94\xaa\x58\x11\x19\xfd\xed\x88\xea\x08\x69\x8f\x7f\x50\xde\x69" "\x2f\x6b\x75\xa1\x8e\xbc\x17\x03\x58\xd5\xb5\x02\x18\x4e\xde\xa7" "\xe1\xde\x30\xfa\x61\x2a\xee\x21\x85\xe7\x13\x14\x3a\x45\xd0\x8b" "\xc7\x20\x38\x2c\xc2\xdd\x05\x95\xde\x42\x02\xb6\xad\x2e\x24\xeb" "\x68\x31\x56\xa8\x80\x5a\x4f\xd7\x9d\x5f\x91\xf6\x3d\x8c\xb1\x21" "\x82\x3a\x35\xea\x38\x1d\xb7\x1a\x0e\x7a\xa0\x2b\x8d\x23\x55\xd4" "\x2e\x78\x26\x08\x0b\xab\xc1\x53\xf3\xc8\xa0\x96\x88\x5d\x6b\x37" "\x33\xc0\xbc\x30\xd3\x26\x4e\xb2\xb1\x66\x45\x08\xc0\x92\xfc\x0b" "\x6c\xc2\x40\x2b\x6b\x16\xf2\x14\x09\xdd\x2c\xb3\x8c\xdf\x17\x4e" "\xd6\x02\x63\x6e\x94\x50\x5f\xfa\x05\x0d\x44\x1c\xbf\x84\x16\xfe" "\xdd\x7c\x04\xa1\x77\x6b\x31\x42\xdb\xf8\x7a\xe1\x81\x5c\x8b\xc7" "\xa5\x8f\xde\x42\xca\x0f\x5e\xd6\xc0\x11\x32\xe0\x51\x02\xb3\x10" "\x82\xa7\xa0\xd0\xfa\x0d\x8d\xca\x04\x9d\x1e\x92\x81\x70\xc1\xd5" "\x11\xfc\x0d\x0e\x51\x73\x60\x87\xeb\x4b\xec\x0d\xa2\x4b\x18\x0f" "\xe3\x56\xff\xfc\x6b\x30\x6a\x71\x26\xea\x86\xe8\xf6\xfa\x5f\xa2" "\xe7\x65\xb7\x62\xa1\x86\x97\x7b\x86\x17\xc6\x90\x10\xe6\x17\x92" "\xee\x19\xb5\x4f\xfe\xf6\xeb\xa9\xa9\xb1\x5b\xa1\xa0\xb9\x7c\x76" "\xda\xb9\x4f\x45\xfe\x59\xde\x0b\x56\x31\x36\xbc\x3b\x12\x2d\x0b" "\x15\x85\x82\x1b\x6c\xcb\xbe\xe4\xa1\xdc\x59\x5c\xd0\x56\xb8\xc7" "\xd9\x5c\xfb\x94\x8d\x31\x34\xe6\x37\x8f\xf9\x79\x5f\x72\x28\x05" "\xf4\xe9\x59\x5e\x17\x91\x98\xcd\xbf\xf3\x07\xc7\x4d\x76\x64\x0f" "\xd6\xc5\x5b\xbd\xf8\xe8\x15\x03\xdc\xf4\x7c\x70\x0e\xea\x68\x3e" "\xf7\xa9\x0f\x78\x3c\x68\x4c\x66\x3b\x13\x25\xf5\xb3\xd6\xbb\x90" "\x71\xeb\xaf\x6e\x5e\x6a\x3b\x68\xe1\xd3\x41\xae\x62\x0b\xf8\x9d" "\xd4\xb5\xc8\x17\x83\xf6\x8e\x92\x3a\x67\x96\xeb\x03\x57\x62\xb1" "\x8e\x32\x3b\x79\x0f\x42\x37\x93\x6d\x3e\x1c\xf1\xdd\xd6\xd5\x51" "\xb3\x59\x1f\x2a\x2d\x9d\x38\x2a\x6f\x4a\xb9\x32\x3c\xb2\x69\xc3" "\x45\xdd\x9e\x19\x38\xb4\xcc\xe8\x95\xd2\x8b\x92\x6e\x4d\xe2\xc2" "\x8d\xe6\x99\xba\xa0\x24\x21\xcd\x6a\x8c\x85\xd5\xa7\x36\xac\x49" "\x0b\xe3\xfa\x8b\x48\xff\xda\x80\x68\x08\x69\x17\x76" /* This is the MAC (last 16 bytes) */ "\xd7\xcc\xfb\xab\x9c\x60\xb8\x7b\x2e\xa2\x57\xaa\x11\xdf\x63\x52" ; // Cipher for the above, using CKM_AES_GCM const uint8_t cipher_data_gcm[CIPHER_DATA_LEN] __nonstring = "\x8b\xf4\x04\xaa\xd4\x9e\x61\xc1\xb7\x5a\x68\x42\x59\x13\xcd\xf9" "\x8b\x0c\x31\x4c\x13\xfd\xbf\xc9\x7d\x9f\xe0\x3e\x8c\x1a\xfb\x2c" "\xf5\x4c\x47\x37\x01\x2c\x5f\xa1\x07\x12\xd5\x46\x59\x3c\x94\x28" "\x60\x71\xb9\x74\x9b\xfa\x33\xa4\x87\xa0\xd6\x1a\xce\xf3\x5b\x19" "\xf4\xc7\x9e\xb8\x22\xe0\xfb\xf3\x44\x43\x47\x25\x14\x66\xb3\x95" "\xca\xa0\xd7\x55\x08\x0b\xaf\x45\x75\xa6\x95\x22\x0a\x28\xeb\xf9" "\x5d\xbe\x8e\xb0\x56\x48\x68\x4c\x52\xbf\x5b\xec\xad\x65\x5d\xa6" "\x28\x68\xeb\xdf\x55\x31\xc2\x0d\xef\xe4\xec\x85\x84\x56\xda\xa3" "\x1d\x84\x98\x64\xf4\x54\xb6\x9b\x23\xc4\x09\x51\x40\xe3\xd6\x48" "\x85\xaf\x89\x47\xb6\xdf\x00\x94\x98\x12\x36\x34\xb8\xa2\xcd\x3a" "\xc8\x75\xde\x4f\xcb\x02\x19\x91\x7a\x8e\xbc\x53\x85\x43\xbe\xae" "\xeb\x6c\x17\xef\xd4\x89\x61\x66\x87\x33\xe4\x8d\xc0\x55\xb6\xfc" "\x29\x88\x1b\x01\x1f\x9d\xfc\xdb\x04\x55\xaf\xa1\xb8\x91\x72\x83" "\x41\x73\x3d\x5b\x87\x7d\xed\x90\x06\x62\x0e\xf1\xdf\x9e\x73\xdd" "\x12\xdb\x3f\x9f\x07\x66\x60\x8b\x8c\xea\x38\xc8\xf5\x63\x98\x0e" "\x37\x69\xa9\x6a\x3c\x03\xe9\x0a\x16\x81\x44\xb9\xa5\xb3\x42\x19" "\x42\x93\x97\x5f\xc6\x26\x37\xab\x47\x65\x1b\x30\x73\x1b\x60\x4c" "\x24\x43\xe7\xa6\x19\x24\x70\x75\xb3\xa6\x2d\x1e\xfe\xa7\xa8\xc0" "\x63\x56\x2c\x48\x8c\x2f\xa9\x8b\x2e\xca\x01\xff\x84\xb8\xd5\x39" "\x44\x61\xcb\xd7\x6d\xfd\x50\xb1\x63\x92\x9b\x1e\x9f\x79\x5b\x1a" "\x98\x12\x82\x84\x18\xa3\x9f\x9c\xfa\xee\xd9\x66\x54\x1b\x62\x04" "\x23\x08\x52\xe0\x8b\xf0\xc1\x64\x05\x3f\xf5\xf8\xa5\x0f\x13\x9e" "\x80\x3b\xb6\x3c\xbb\x68\xb2\xad\xad\x09\xf5\x68\x62\x24\xc9\x27" "\x5c\x27\xb0\xdd\x84\xde\x65\xef\xa1\xcc\xd6\x8b\x9c\x5a\x6a\x9f" "\x87\xc1\xd5\xdf\xd8\x50\x33\x87\xce\x3d\x2f\x28\x2b\x60\x88\xd3" "\xac\x19\x03\x65\x26\xc5\x5d\x09\xb3\xc4\x07\x9b\x04\x11\x4b\xc1" "\x33\xc9\x7c\x45\xc5\xeb\x7f\xa9\xb6\xb4\x1a\x90\x53\x94\xe4\xe9" "\xb6\xa5\x48\x77\xc4\xc5\x72\xed\x70\x00\x14\x34\x80\x18\x92\x9b" "\x38\x18\x86\x03\x76\x38\x36\xf4\xcf\xac\x74\x3e\xaf\xa8\x06\x7d" "\x04\xb4\x57\xdc\x88\x04\xb6\xee\x41\x0e\xda\x9c\xc3\x65\xe9\x23" "\xb2\xed\xd3\x7b\xd4\x7f\x28\x36\xe7\x37\x83\x5c\x9e\xfa\xb9\x17" "\x8b\xb6\xbe\x9f\xe3\x72\x25\x80\x42\x6f\xd7\xd6\xc5\x66\x20\x5c" "\xfe\xc8\xcd\x16\x1b\x6b\xbb\xf0\x1f\x88\xd7\xfa\x2d\xe4\x6f\xa4" "\xf5\x48\x85\x33\x65\x39\x2b\xef\xe8\x3d\xd2\x30\x48\xed\xde\x45" "\x0b\xb0\xf3\x54\x5b\xff\x37\x41\xc3\x6c\x62\x53\x33\x42\x59\x04" "\xbb\x5e\xcf\x5f\x69\x00\xf5\xa8\x23\xdb\x5b\xc9\x10\xe6\x4f\x9e" "\x38\xfd\xb1\x1b\x4a\x35\xe4\xf1\x91\x1d\x01\xaa\x1b\x99\xfc\x5c" "\x88\xcb\x78\xb8\xc9\x9b\x45\xba\x29\x12\x50\x7f\xe2\xa0\x52\xcd" "\xe9\xc2\x34\xbe\x94\xf9\xc6\x84\xe2\x3e\x60\xdc\x39\xc0\x23\xa8" "\xa3\x31\xc1\x9b\x7e\x09\x70\x4f\x59\xd6\x37\x76\xda\x93\x39\x42" "\xd0\xba\xc8\xb0\xd7\xe4\x7c\xd3\x52\x28\x46\x9e\x3d\x9e\x2a\xb2" "\x96\x92\xcf\xc6\x6f\x99\x50\x21\x93\x57\x80\x37\x7c\xb8\x79\x7f" "\xed\xb0\xcd\x3c\x02\xa7\xa6\x9b\xf9\xa3\x0c\xcc\x5a\x40\x79\x76" "\xf5\xc9\x0d\xcf\xa8\x9b\xb3\x8b\xcd\x29\x31\x8c\x90\x89\x92\x7b" "\x38\x18\x54\x6b\xee\x25\xee\x91\xab\x3b\xf7\x6c\xee\x8e\x30\xad" "\xf0\x3c\xac\x04\x3f\xac\x39\x2f\xee\x12\x06\x11\x89\xc6\xd6\x6c" "\xc4\x7c\xa6\x1f\x4f\x5c\x40\x79\x8a\xd8\xe2\x48\x6d\xa7\x32\xd7" "\x52\x92\x57\x9e\xc7\x17\x59\x92\xdb\xa4\x22\x21\x8f" /* This is the MAC (last 16 bytes) */ "\xd6\xa2\xca\x10\x72\x15\x82\xe7\x02\x8c\x3f\xa3\x77\x2a\x42\x13" ; #ifdef __cplusplus } #endif #endif /* _TEST_DATA_H */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "test_data.h" #include "utils.h" /* * Test program for the interfaces used in * smb3_decrypt_command() */ int do_decrypt(char *outbuf, size_t *outlen, const uint8_t *inbuf, size_t inlen, int mid) { smb_enc_ctx_t ctx; uio_t uio_in; uio_t uio_out; iovec_t iov_in[4]; iovec_t iov_out[4]; int rc; bzero(&ctx, sizeof (ctx)); ctx.mech.mechanism = mid; // CKM_AES_CCM or CKM_AES_GCM switch (mid) { case CKM_AES_CCM: smb3_crypto_init_ccm_param(&ctx, (uint8_t *)nonce, 11, (uint8_t *)authdata, 16, inlen); break; case CKM_AES_GCM: smb3_crypto_init_gcm_param(&ctx, (uint8_t *)nonce, 12, (uint8_t *)authdata, 16); break; default: return (1); } rc = smb3_decrypt_init(&ctx, (uint8_t *)keydata, 16); if (rc != 0) return (rc); make_uio((void *)inbuf, inlen, &uio_in, iov_in, 4); make_uio(outbuf, *outlen, &uio_out, iov_out, 4); *outlen = uio_out.uio_resid; rc = smb3_decrypt_uio(&ctx, &uio_in, &uio_out); *outlen -= uio_out.uio_resid; smb3_enc_ctx_done(&ctx); return (rc); } char outbuf[CLEAR_DATA_LEN]; void test_decrypt(const uint8_t *cipher, int mid) { size_t outlen; int rc; outlen = sizeof (outbuf); rc = do_decrypt(outbuf, &outlen, cipher, CIPHER_DATA_LEN, mid); if (rc != 0) { printf("FAIL: decrypt rc= %d\n"); return; } if (outlen != CLEAR_DATA_LEN) { printf("FAIL: out len = %d (want %d)\n", outlen, CLEAR_DATA_LEN); return; } if (memcmp(outbuf, clear_data_ref, CLEAR_DATA_LEN) != 0) { printf("FAIL: ciphertext:\n"); hexdump((uchar_t *)outbuf, CLEAR_DATA_LEN); return; } printf("PASS mid=0x%x\n", mid); } int main(int argc, char *argv[]) { test_decrypt(cipher_data_ccm, CKM_AES_CCM); test_decrypt(cipher_data_gcm, CKM_AES_GCM); return (0); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "test_data.h" #include "utils.h" /* * Test program for the interfaces used in * smb3_encrypt_reply() */ int do_encrypt(uint8_t *outbuf, size_t *outlen, const char *inbuf, size_t inlen, int mid) { smb_enc_ctx_t ctx; uio_t uio_in; uio_t uio_out; iovec_t iov_in[4]; iovec_t iov_out[4]; int rc; bzero(&ctx, sizeof (ctx)); ctx.mech.mechanism = mid; // CKM_AES_CCM or CKM_AES_GCM switch (mid) { case CKM_AES_CCM: smb3_crypto_init_ccm_param(&ctx, (uint8_t *)nonce, 11, (uint8_t *)authdata, 16, inlen); break; case CKM_AES_GCM: smb3_crypto_init_gcm_param(&ctx, (uint8_t *)nonce, 12, (uint8_t *)authdata, 16); break; default: return (1); } rc = smb3_encrypt_init(&ctx, (uint8_t *)keydata, 16); if (rc != 0) return (rc); make_uio((void *)inbuf, inlen, &uio_in, iov_in, 4); make_uio(outbuf, *outlen, &uio_out, iov_out, 4); *outlen = uio_out.uio_resid; rc = smb3_encrypt_uio(&ctx, &uio_in, &uio_out); *outlen -= uio_out.uio_resid; smb3_enc_ctx_done(&ctx); return (rc); } uint8_t outbuf[CIPHER_DATA_LEN]; void test_encrypt(const uint8_t *ref, int mid) { size_t outlen; int rc; outlen = sizeof (outbuf); rc = do_encrypt(outbuf, &outlen, clear_data_ref, clear_data_len, mid); if (rc != 0) { printf("FAIL: encrypt rc= %d\n"); return; } if (outlen != CIPHER_DATA_LEN) { printf("FAIL: out len = %d (want %d)\n", outlen, CIPHER_DATA_LEN); return; } if (memcmp(outbuf, ref, CIPHER_DATA_LEN) != 0) { printf("FAIL: ciphertext:\n"); hexdump(outbuf, CIPHER_DATA_LEN); return; } printf("PASS mid=0x%x\n", mid); } int main(int argc, char *argv[]) { test_encrypt(cipher_data_ccm, CKM_AES_CCM); test_encrypt(cipher_data_gcm, CKM_AES_GCM); return (0); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ #include #include #include #include #include "utils.h" void hexdump(const uchar_t *buf, int len) { int idx; char ascii[24]; char *pa = ascii; bzero(ascii, sizeof (ascii)); idx = 0; while (len--) { if ((idx & 15) == 0) { printf("%04X: ", idx); pa = ascii; } if (*buf > ' ' && *buf <= '~') *pa++ = *buf; else *pa++ = '.'; printf("%02x ", *buf++); idx++; if ((idx & 3) == 0) { *pa++ = ' '; (void) putchar(' '); } if ((idx & 15) == 0) { *pa = '\0'; printf("%s\n", ascii); } } if ((idx & 15) != 0) { *pa = '\0'; /* column align the last ascii row */ while ((idx & 15) != 0) { if ((idx & 3) == 0) (void) putchar(' '); printf(" "); idx++; } printf("%s\n", ascii); } } /* * Provide a real function (one that prints something) to replace * the stub in libfakekernel. This prints cmn_err() messages. */ void fakekernel_putlog(char *msg, size_t len, int flags) { (void) fwrite(msg, 1, len, stdout); (void) fflush(stdout); } /* * Build a UIO for the input or output buffer * Arbitrarily splits into 2 segs for testing uio. */ void make_uio(void *buf, size_t buflen, uio_t *uio, iovec_t *iov, int iovmax) { size_t maxseg = 512; size_t tlen; int i; bzero(uio, sizeof (*uio)); uio->uio_resid = buflen; uio->uio_segflg = UIO_SYSSPACE; for (i = 0; i < iovmax; i++) { if (buflen <= 0) break; tlen = buflen; if (tlen > maxseg) tlen = maxseg; iov[i].iov_base = buf; iov[i].iov_len = tlen; buf += tlen; buflen -= tlen; } uio->uio_iov = iov; uio->uio_iovcnt = i; } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ #ifndef _UTILS_H #define _UTILS_H #include #include #ifdef __cplusplus extern "C" { #endif void hexdump(const uint8_t *buf, int len); void make_uio(void *, size_t, uio_t *, iovec_t *, int); #ifdef __cplusplus } #endif #endif /* _UTILS_H */ # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2018 Nexenta Systems, Inc. All rights reserved. # set -o emacs setenv ROOT ${CODEMGR_WS}/proto/root_i386 setenv LD_LIBRARY_PATH ${ROOT}/usr/lib/smbsrv:${ROOT}/usr/lib:${ROOT}/lib echo debug ${ROOT}/usr/lib/smbsrv/test-msgbuf # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2018 Nexenta Systems, Inc. All rights reserved. # PROG= test-msgbuf OBJS_LOCAL= test_main.o test_misc.o test_conv.o \ test_mbmarshal.o test_msgbuf.o OBJS_SMBSRV= smb_mbuf_marshaling.o smb_mbuf_util.o smb_alloc.o OBJS_SMBCMN= smb_msgbuf.o smb_string.o smb_utf8.o OBJS= ${OBJS_LOCAL} ${OBJS_SMBSRV} ${OBJS_SMBCMN} SMBSRV_SRCDIR=../../../uts/common/fs/smbsrv SMBSRV_CMNDIR=../../../common/smbsrv SRCS= ${OBJS_LOCAL:.o=.c} \ ${OBJS_SMBSRV:%.o=${SMBSRV_SRCDIR}/%.c} \ ${OBJS_SMBCMN:%.o=${SMBSRV_CMNDIR}/%.c} include ../../Makefile.cmd include ../../Makefile.ctf # Note: need our sys includes _before_ ENVCPPFLAGS, proto etc. CPPFLAGS.first += -I. CPPFLAGS.first += -I../../../lib/libfakekernel/common CPPFLAGS.first += -I../../../lib/smbsrv/libfksmbsrv/common INCS += -I../../../uts/common INCS += -I../../../uts/common/smbsrv INCS += -I../../../common/smbsrv CSTD= $(CSTD_GNU99) CFLAGS += $(CCVERBOSE) CFLAGS64 += $(CCVERBOSE) CERRWARN += -Wno-parentheses CPPFLAGS += -D_REENTRANT CPPFLAGS += -DTESTJIG CPPFLAGS += -Dsyslog=smb_syslog CPPFLAGS += -D_LARGEFILE64_SOURCE=1 # Always debug here CPPFLAGS += -DDEBUG CPPFLAGS += $(INCS) LDFLAGS += $(ZNOLAZYLOAD) LDFLAGS += '-R$$ORIGIN/..' LDLIBS += -lfakekernel -lcmdutils ROOTSMBDDIR = $(ROOTLIB)/smbsrv ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%) .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) $(POST_PROCESS) clean: -$(RM) $(OBJS) include ../../Makefile.targ install: all $(ROOTSMBDFILE) test_main.o : test_main.c $(CC) $(CFLAGS) $(CPPFLAGS) -D_KMEMUSER -c test_main.c $(POST_PROCESS_O) test_misc.o : test_misc.c $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \ -I../../../uts/common/smbsrv \ -I../../../common/smbsrv -c test_misc.c $(POST_PROCESS_O) # OBJS_SMBSRV %.o: ../../../uts/common/fs/smbsrv/%.c $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \ -I../../../uts/common/smbsrv \ -I../../../common/smbsrv -c $< $(POST_PROCESS_O) # OBJS_SMBCMN %.o: ../../../common/smbsrv/%.c $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \ -I../../../uts/common/smbsrv \ -I../../../common/smbsrv -c $< $(POST_PROCESS_O) $(ROOTSMBDDIR)/%: % $(INS.file) #!/bin/sh # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2018 Nexenta Systems, Inc. All rights reserved. # # Helper program to run test-msgbuf (unit test program) # using binaries from the proto area. [ -n "$CODEMGR_WS" ] || { echo "Need a buildenv to set CODEMGR_WS=..." exit 1; } ROOT=${CODEMGR_WS}/proto/root_i386 LD_LIBRARY_PATH=$ROOT/usr/lib/smbsrv:$ROOT/usr/lib:$ROOT/lib export LD_LIBRARY_PATH export UMEM_DEBUG=default # run with the passed options exec $ROOT/usr/lib/smbsrv/test-msgbuf "$@" /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright 2017-2021 Tintri by DDN, Inc. All rights reserved. */ #ifndef _SYS_SDT_H #define _SYS_SDT_H #ifdef __cplusplus extern "C" { #endif /* * For this test program define any dtrace probes out of existence. */ #define DTRACE_PROBE(a) ((void)0) #define DTRACE_PROBE1(a, ...) ((void)0) #define DTRACE_PROBE2(a, ...) ((void)0) #define DTRACE_PROBE3(a, ...) ((void)0) #define SET_ERROR(err) (void)0) #ifdef __cplusplus } #endif #endif /* _SYS_SDT_H */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * Test conversion of strings UTF-8 to/from UTF-16 etc. * * This tests both 16-bit unicode symbols (UCS-2) and so called * "enhanced" unicode symbols such as the "poop emoji" that are * above 65535 and encode to four bytes as UTF-8. */ #include #include #include #include #include #include #include "test_defs.h" #define U_FW_A 0xff21 // full-width A (A) static const char fwA[4] = "\xef\xbc\xa1"; #define U_POOP 0x1f4a9 // poop emoji (💩) static const char poop[5] = "\xf0\x9f\x92\xa9"; static char mbsa[] = "A\xef\xbc\xa1."; // A fwA . (5) static char mbsp[] = "P\xf0\x9f\x92\xa9."; // P poop . (6) static smb_wchar_t wcsa[] = { 'A', U_FW_A, '.', 0 }; // (3) static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4) static void conv_wctomb() { char mbs[8]; int len; len = smb_wctomb(mbs, U_FW_A); if (len != 3) { printf("Fail: conv_wctomb fwA ret=%d\n", len); return; } mbs[len] = '\0'; if (strcmp(mbs, fwA)) { printf("Fail: conv_wctomb fwA cmp:\n"); hexdump((uchar_t *)mbs, len+1); return; } len = smb_wctomb(mbs, U_POOP); if (len != 4) { printf("Fail: conv_wctomb poop ret=%d\n", len); return; } mbs[len] = '\0'; if (strcmp(mbs, poop)) { printf("Fail: conv_wctomb poop cmp:\n"); hexdump((uchar_t *)mbs, len+1); return; } /* null wc to mbs should return 1 and put a null */ len = smb_wctomb(mbs, 0); if (len != 1) { printf("Fail: conv_wctomb null ret=%d\n", len); return; } if (mbs[0] != '\0') { printf("Fail: conv_wctomb null cmp:\n"); hexdump((uchar_t *)mbs, len+1); return; } printf("Pass: conv_wctomb\n"); } static void conv_mbtowc() { uint32_t wch = 0; int len; /* * The (void *) cast here is to let this build both * before and after an interface change in smb_mbtowc * (uint16_t vs uint32_t) */ len = smb_mbtowc((void *)&wch, fwA, 4); if (len != 3) { printf("Fail: conv_mbtowc fwA ret=%d\n", len); return; } if (wch != U_FW_A) { printf("Fail: conv_mbtowc fwA cmp: 0x%x\n", wch); return; } len = smb_mbtowc((void *)&wch, poop, 4); // poop emoji if (len != 4) { printf("Fail: conv_mbtowc poop ret=%d\n", len); return; } if (wch != U_POOP) { printf("Fail: conv_mbtowc poop cmp: 0x%x\n", wch); return; } /* null mbs to wc should return 0 (and set wch=0) */ len = smb_mbtowc((void *)&wch, "", 4); if (len != 0) { printf("Fail: conv_mbtowc null ret=%d\n", len); return; } if (wch != 0) { printf("Fail: conv_mbtowc null cmp: 0x%x\n", wch); return; } printf("Pass: conv_mbtowc\n"); } static void conv_wcstombs() { char tmbs[16]; int len; len = smb_wcstombs(tmbs, wcsa, sizeof (tmbs)); if (len != 5) { printf("Fail: conv_wcstombs A ret=%d\n", len); return; } if (strcmp(tmbs, mbsa)) { printf("Fail: conv_wcstombs A cmp:\n"); hexdump((uchar_t *)tmbs, len+2); return; } len = smb_wcstombs(tmbs, wcsp, sizeof (tmbs)); if (len != 6) { printf("Fail: conv_wcstombs f ret=%d\n", len); return; } if (strcmp(tmbs, mbsp)) { printf("Fail: conv_wcstombs f cmp:\n"); hexdump((uchar_t *)tmbs, len+2); return; } printf("Pass: conv_wcstombs\n"); } static void conv_mbstowcs() { smb_wchar_t twcs[8]; uint32_t wch = 0; int len; len = smb_mbstowcs(twcs, mbsa, sizeof (twcs)); if (len != 3) { printf("Fail: conv_mbstowcs A ret=%d\n", len); return; } if (memcmp(twcs, wcsa, len+2)) { printf("Fail: conv_mbstowcs A cmp: 0x%x\n", wch); hexdump((uchar_t *)twcs, len+2); return; } len = smb_mbstowcs(twcs, mbsp, sizeof (twcs)); if (len != 4) { printf("Fail: conv_mbstowcs P ret=%d\n", len); return; } if (memcmp(twcs, wcsp, len+2)) { printf("Fail: conv_mbstowcs P cmp: 0x%x\n", wch); hexdump((uchar_t *)twcs, len+2); return; } printf("Pass: conv_mbstowcs\n"); } /* * An OEM string that will require iconv. */ static uchar_t fubar_oem[] = "F\201bar"; // CP850 x81 (ü) static char fubar_mbs[] = "F\303\274bar"; // UTF8 xC3 xBC static void conv_oemtombs() { char tmbs[16]; int len; len = smb_oemtombs(tmbs, (uchar_t *)"foo", 4); if (len != 3) { printf("Fail: conv_wctomb foo ret=%d\n", len); return; } if (strcmp(tmbs, "foo")) { printf("Fail: conv_wctomb foo cmp:\n"); hexdump((uchar_t *)tmbs, len+1); return; } len = smb_oemtombs(tmbs, fubar_oem, 7); if (len != 6) { printf("Fail: conv_oemtombs fubar ret=%d\n", len); return; } if (strcmp(tmbs, fubar_mbs)) { printf("Fail: conv_oemtombs fubar cmp:\n"); hexdump((uchar_t *)tmbs, len+1); return; } printf("Pass: conv_oemtombs\n"); } static void conv_mbstooem() { uint8_t oemcs[8]; uint32_t wch = 0; int len; len = smb_mbstooem(oemcs, "foo", 8); if (len != 3) { printf("Fail: conv_mbstooem foo ret=%d\n", len); return; } if (memcmp(oemcs, "foo", len+1)) { printf("Fail: conv_mbstooem P cmp: 0x%x\n", wch); hexdump((uchar_t *)oemcs, len+1); return; } len = smb_mbstooem(oemcs, fubar_mbs, 8); if (len != 5) { printf("Fail: conv_mbstooem fubar ret=%d\n", len); return; } if (memcmp(oemcs, (char *)fubar_oem, len+1)) { printf("Fail: conv_mbstooem fubar cmp: 0x%x\n", wch); hexdump((uchar_t *)oemcs, len+1); return; } len = smb_mbstooem(oemcs, mbsp, 8); if (len != 3) { printf("Fail: conv_mbstooem poop ret=%d\n", len); return; } if (memcmp(oemcs, "P?.", len+1)) { printf("Fail: conv_mbstooem poop cmp: 0x%x\n", wch); hexdump((uchar_t *)oemcs, len+1); return; } printf("Pass: conv_mbstooem\n"); } static void conv_sbequiv_strlen() { int len; len = (int)smb_sbequiv_strlen("a"); if (len != 1) { printf("Fail: conv_sbequiv_strlen (a) len=%d\n", len); return; } len = (int)smb_sbequiv_strlen(fubar_mbs); if (len != strlen((char *)fubar_oem)) { printf("Fail: conv_sbequiv_strlen (fubar) len=%d\n", len); return; } len = (int)smb_sbequiv_strlen(mbsp); if (len != 3) { // "P?." printf("Fail: conv_sbequiv_strlen (poop) len=%d\n", len); return; } printf("Pass: conv_sbequiv_strlen\n"); } static void conv_wcequiv_strlen() { int len; len = (int)smb_wcequiv_strlen("a"); if (len != 2) { printf("Fail: conv_wcequiv_strlen (a) len=%d\n", len); return; } len = (int)smb_wcequiv_strlen(fwA); if (len != 2) { printf("Fail: conv_wcequiv_strlen (fwA) len=%d\n", len); return; } len = (int)smb_wcequiv_strlen(poop); if (len != 4) { printf("Fail: conv_wcequiv_strlen (poop) len=%d\n", len); return; } printf("Pass: conv_wcequiv_strlen\n"); } void test_conv() { conv_wctomb(); conv_mbtowc(); conv_wcstombs(); conv_mbstowcs(); conv_oemtombs(); conv_mbstooem(); conv_sbequiv_strlen(); conv_wcequiv_strlen(); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _TEST_DEFS_H #define _TEST_DEFS_H /* * Describe the purpose of the file here. */ #ifdef __cplusplus extern "C" { #endif extern void hexdump(const uchar_t *buf, int len); extern void test_conv(void); extern void test_mbmarshal(void); extern void test_msgbuf(void); #ifdef __cplusplus } #endif #endif /* _TEST_DEFS_H */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. * Copyright 2019 Joyent, Inc. */ /* * Test & debug program for smb_msgbuf.c and smb_mbuf_marshaling.c */ #include #include #include #include #include #include #include #include "test_defs.h" int main(int argc, char *argv[]) { test_conv(); test_mbmarshal(); test_msgbuf(); return (0); } void hexdump(const uchar_t *buf, int len) { int idx; char ascii[24]; char *pa = ascii; memset(ascii, '\0', sizeof (ascii)); idx = 0; while (len--) { if ((idx & 15) == 0) { printf("%04X: ", idx); pa = ascii; } if (*buf > ' ' && *buf <= '~') *pa++ = *buf; else *pa++ = '.'; printf("%02x ", *buf++); idx++; if ((idx & 3) == 0) { *pa++ = ' '; (void) putchar(' '); } if ((idx & 15) == 0) { *pa = '\0'; printf("%s\n", ascii); } } if ((idx & 15) != 0) { *pa = '\0'; /* column align the last ascii row */ while ((idx & 15) != 0) { if ((idx & 3) == 0) (void) putchar(' '); printf(" "); idx++; } printf("%s\n", ascii); } } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018-2021 Tintri by DDN, Inc. All rights reserved. */ /* * Test putting/getting unicode strings in mbchains. */ #include #include #include #include #include #include #include #include "test_defs.h" static char mbsa[] = "A\xef\xbc\xa1."; // A fwA . (5) static char mbsp[] = "P\xf0\x9f\x92\xa9."; // P poop . (6) static smb_wchar_t wcsa[] = { 'A', 0xff21, '.', 0 }; // (3) static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4) smb_session_t test_ssn; smb_request_t test_sr; /* * Put ASCII string with NULL */ static void mbm_put_a0() { uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); rc = smb_mbc_encodef(mbc, "sw", "one", 42); if (rc != 0) { printf("Fail: mbm_put_a0 encode\n"); goto out; } if (mbc->chain->m_len != 6) { printf("Fail: mbm_put_a0 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 6)) { printf("Fail: mbm_put_a0 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 6); return; } printf("Pass: mbm_put_a0\n"); out: smb_mbc_free(mbc); } /* * Put ASCII string, no NULL */ static void mbm_put_a1() { uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); rc = smb_mbc_encodef(mbc, "4sw", "one.", 42); if (rc != 0) { printf("Fail: mbm_put_a1 encode\n"); goto out; } if (mbc->chain->m_len != 6) { printf("Fail: mbm_put_a1 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 6)) { printf("Fail: mbm_put_a1 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 6); return; } printf("Pass: mbm_put_a1\n"); out: smb_mbc_free(mbc); } static void mbm_put_apad() { uint8_t wire[] = { 'o', 'n', 'e', 0, 0 }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); /* Encode with wire length > strlen */ rc = smb_mbc_encodef(mbc, "5s", "one"); if (rc != 0) { printf("Fail: mbm_put_apad encode\n"); goto out; } if (mbc->chain->m_len != 5) { printf("Fail: mbm_put_apad len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 5)) { printf("Fail: mbm_put_apad cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 5); return; } printf("Pass: mbm_put_apad\n"); out: smb_mbc_free(mbc); } static void mbm_put_atrunc1() { uint8_t wire[] = { 'o', 'n', 'e', 't', }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); /* Encode with wire length < strlen */ rc = smb_mbc_encodef(mbc, "4s", "onetwo"); if (rc != 0) { printf("Fail: mbm_put_atrunc1 encode\n"); goto out; } /* Trunc should put exactly 4 */ if (mbc->chain->m_len != 4) { printf("Fail: mbm_put_atrunc1 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 4)) { printf("Fail: mbm_put_atrunc1 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 4); return; } printf("Pass: mbm_put_atrunc1\n"); out: smb_mbc_free(mbc); } static void mbm_put_atrunc2() { uint8_t wire[] = { 'o', 'n', 'e', 't', 0 }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(4); /* Encode with wire length < strlen */ rc = smb_mbc_encodef(mbc, "s", "onetwo"); if (rc != 1) { printf("Fail: mbm_put_atrunc2 encode rc=%d\n", rc); goto out; } /* Trunc should put exactly 4 */ if (mbc->chain->m_len != 4) { printf("Fail: mbm_put_atrunc2 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 5)) { printf("Fail: mbm_put_atrunc2 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 4); return; } printf("Pass: mbm_put_atrunc2\n"); out: smb_mbc_free(mbc); } /* * Put unicode string with NULL */ static void mbm_put_u0() { uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); rc = smb_mbc_encodef(mbc, "Uw", "one", 42); if (rc != 0) { printf("Fail: mbm_put_u0 encode\n"); goto out; } if (mbc->chain->m_len != 10) { printf("Fail: mbm_put_u0 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 10)) { printf("Fail: mbm_put_u0 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 10); return; } printf("Pass: mbm_put_u0\n"); out: smb_mbc_free(mbc); } /* * Put unicode string, no NULL */ static void mbm_put_u1() { uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); rc = smb_mbc_encodef(mbc, "8Uw", "one.", 42); if (rc != 0) { printf("Fail: mbm_put_u1 encode\n"); goto out; } if (mbc->chain->m_len != 10) { printf("Fail: mbm_put_u1 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 10)) { printf("Fail: mbm_put_u1 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 10); return; } printf("Pass: mbm_put_u1\n"); out: smb_mbc_free(mbc); } static void mbm_put_u3() { mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); rc = smb_mbc_encodef(mbc, "U", mbsa); if (rc != 0) { printf("Fail: mbm_put_u3 encode\n"); goto out; } if (mbc->chain->m_len != 8) { printf("Fail: mbm_put_u3 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wcsa, 8)) { printf("Fail: mbm_put_u3 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 8); return; } printf("Pass: mbm_put_u3\n"); out: smb_mbc_free(mbc); } static void mbm_put_u4() { mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); rc = smb_mbc_encodef(mbc, "U", mbsp); if (rc != 0) { printf("Fail: mbm_put_u4 encode\n"); goto out; } if (mbc->chain->m_len != 10) { printf("Fail: mbm_put_u4 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wcsp, 10)) { printf("Fail: mbm_put_u4 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 10); return; } printf("Pass: mbm_put_u4\n"); out: smb_mbc_free(mbc); } static void mbm_put_upad() { uint16_t wire[] = { 'o', 'n', 'e', 0, 0 }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); /* Encode with wire length > strlen */ rc = smb_mbc_encodef(mbc, "10U", "one"); if (rc != 0) { printf("Fail: mbm_put_upad encode\n"); goto out; } if (mbc->chain->m_len != 10) { printf("Fail: mbm_put_upad len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 10)) { printf("Fail: mbm_put_upad cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 10); return; } printf("Pass: mbm_put_upad\n"); out: smb_mbc_free(mbc); } static void mbm_put_utrunc1() { uint16_t wire[] = { 'o', 'n', 'e', 't' }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(100); /* Encode with wire length < strlen */ rc = smb_mbc_encodef(mbc, "8U", "onetwo"); if (rc != 0) { printf("Fail: mbm_put_utrunc1 encode\n"); goto out; } /* Trunc should put exactly 8 */ if (mbc->chain->m_len != 8) { printf("Fail: mbm_put_utrunc1 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 8)) { printf("Fail: mbm_put_utrunc1 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 8); return; } printf("Pass: mbm_put_utrunc1\n"); out: smb_mbc_free(mbc); } static void mbm_put_utrunc2() { uint16_t wire[] = { 'o', 'n', 'e', 't', 0 }; mbuf_chain_t *mbc; int rc; mbc = smb_mbc_alloc(8); /* Encode with wire length < strlen */ rc = smb_mbc_encodef(mbc, "U", "onetwo"); if (rc != 1) { printf("Fail: mbm_put_utrunc2 encode rc=%d\n", rc); goto out; } /* Trunc should put exactly 8 */ if (mbc->chain->m_len != 8) { printf("Fail: mbm_put_utrunc2 len=%d\n", mbc->chain->m_len); return; } if (memcmp(mbc->chain->m_data, wire, 10)) { printf("Fail: mbm_put_utrunc2 cmp:\n"); hexdump((uchar_t *)mbc->chain->m_data, 8); return; } printf("Pass: mbm_put_utrunc2\n"); out: smb_mbc_free(mbc); } static void mbm_put_mbuf1() { uint8_t wire[] = "onetwo"; mbuf_chain_t mbc, mbc2; int rc; MBC_INIT(&mbc, 16); MBC_INIT(&mbc2, 8); rc = smb_mbc_encodef(&mbc, "3s", "one"); if (rc != 0) { printf("Fail: mbm_put_mbuf1 encode 1 rc=%d\n", rc); goto out; } if (mbc.chain_offset != 3) { printf("Fail: mbm_put_mbuf1 encode 1 len=%d\n", mbc.chain_offset); goto out; } rc = smb_mbc_encodef(&mbc2, "s", "two"); if (rc != 0) { printf("Fail: mbm_put_mbuf1 encode 2 rc=%d\n", rc); goto out; } if (mbc2.chain_offset != 4) { printf("Fail: mbm_put_mbuf1 encode 2 len=%d\n", mbc.chain_offset); goto out; } /* Append */ rc = smb_mbc_encodef(&mbc, "m", mbc2.chain); mbc2.chain = NULL; if (rc != 0) { printf("Fail: mbm_put_mbuf1 encode 3 rc=%d\n", rc); goto out; } if (mbc.chain_offset != 7) { printf("Fail: mbm_put_mbuf1 encode 3 len=%d\n", mbc.chain_offset); goto out; } if (memcmp(mbc.chain->m_data, wire, 7)) { printf("Fail: mbm_put_mbuf1 cmp:\n"); hexdump((uchar_t *)mbc.chain->m_data, 7); goto out; } printf("Pass: mbm_put_mbuf1\n"); out: MBC_FLUSH(&mbc); MBC_FLUSH(&mbc2); } /* * Verify m_prepend works */ static void mbm_put_mbuf2() { uint8_t wire[] = "one.two"; mbuf_chain_t mbc; mbuf_t *m; int len, rc; MBC_INIT(&mbc, 512); rc = smb_mbc_encodef(&mbc, "s", "two"); if (rc != 0) { printf("Fail: mbm_put_mbuf2 encode 1 rc=%d\n", rc); goto out; } if (mbc.chain_offset != 4) { printf("Fail: mbm_put_mbuf2 encode 1 len=%d\n", mbc.chain_offset); goto out; } /* Prepend. Should use prepend space, not allocate. */ m = m_prepend(mbc.chain, 4, M_WAIT); if (m != mbc.chain) { (void) m_free(m); printf("Fail: mbm_put_mbuf2 m_prepend error\n", rc); goto out; } /* Now write into the prepend space. */ bcopy("one.", m->m_data, 4); /* Verify length and content */ len = MBC_LENGTH(&mbc); if (len != 8) { printf("Fail: mbm_put_mbuf2 encode 2 len=%d\n", mbc.chain_offset); goto out; } if (memcmp(mbc.chain->m_data, wire, 7)) { printf("Fail: mbm_put_mbuf2 cmp:\n"); hexdump((uchar_t *)mbc.chain->m_data, 7); goto out; } printf("Pass: mbm_put_mbuf2\n"); out: MBC_FLUSH(&mbc); } /* * Check how mbc_marshal_make_room crosses a message boundary */ static void mbm_put_mbuf3() { mbuf_chain_t mbc; mbuf_t *m; char *p; int rc; MBC_INIT(&mbc, 16384); /* * Encode near the end of the first mbuf * running over into the second. */ m = mbc.chain; mbc.chain_offset = M_TRAILINGSPACE(m) - 4; rc = smb_mbc_encodef(&mbc, "s", "one.two"); if (rc != 0) { printf("Fail: mbm_put_mbuf3 encode 1 rc=%d\n", rc); goto out; } /* Verify first segment */ p = m->m_data + m->m_len - 4; if (memcmp(p, "one.", 4)) { printf("Fail: mbm_put_mbuf3 cmp1:\n"); hexdump((uchar_t *)p, 4); goto out; } /* Verify second segment */ p = m->m_next->m_data; if (memcmp(p, "two", 4)) { printf("Fail: mbm_put_mbuf3 cmp2:\n"); hexdump((uchar_t *)p, 4); goto out; } printf("Pass: mbm_put_mbuf3\n"); out: MBC_FLUSH(&mbc); } /* * Parse an ascii string. */ static void mbm_get_a0() { uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; mbuf_chain_t mbc; char *s; int rc; uint16_t w; bzero(&mbc, sizeof (mbc)); MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); rc = smb_mbc_decodef(&mbc, "%sw", &test_sr, &s, &w); if (rc != 0) { printf("Fail: mbm_get_a0 decode\n"); goto out; } /* * Decode a word after the string to make sure we * end up positioned correctly after the string. */ if (w != 42) { printf("Fail: mbm_get_a0 w=%d\n", w); return; } if (strcmp(s, "one") != 0) { printf("Fail: mbm_get_a0 cmp: <%s>\n", s); return; } printf("Pass: mbm_get_a0\n"); out: MBC_FLUSH(&mbc); } /* * Parse an ascii string, no NULL */ static void mbm_get_a1() { uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; mbuf_chain_t mbc; char *s; int rc; uint16_t w; bzero(&mbc, sizeof (mbc)); MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); rc = smb_mbc_decodef(&mbc, "%3s.w", &test_sr, &s, &w); if (rc != 0) { printf("Fail: mbm_get_a1 decode\n"); goto out; } /* * Decode a word after the string to make sure we * end up positioned correctly after the string. */ if (w != 42) { printf("Fail: mbm_get_a1 w=%d\n", w); return; } if (strcmp(s, "one") != 0) { printf("Fail: mbm_get_a1 cmp: <%s>\n", s); return; } printf("Pass: mbm_get_a1\n"); out: MBC_FLUSH(&mbc); } /* parse exactly to end of data */ static void mbm_get_a2() { uint8_t wire[] = { 'o', 'n', 'e' }; mbuf_chain_t mbc; char *s; int rc; bzero(&mbc, sizeof (mbc)); MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); rc = smb_mbc_decodef(&mbc, "%3s", &test_sr, &s); if (rc != 0) { printf("Fail: mbm_get_a2 decode\n"); goto out; } if (mbc.chain_offset != 3) { printf("Fail: mbm_get_a2 wrong pos\n"); return; } if (strcmp(s, "one") != 0) { printf("Fail: mbm_get_a2 cmp: <%s>\n", s); return; } printf("Pass: mbm_get_a2\n"); out: MBC_FLUSH(&mbc); } /* * Parse a unicode string. */ static void mbm_get_u0() { uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; mbuf_chain_t mbc; char *s; int rc; uint16_t w; bzero(&mbc, sizeof (mbc)); MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); rc = smb_mbc_decodef(&mbc, "%Uw", &test_sr, &s, &w); if (rc != 0) { printf("Fail: mbm_get_u0 decode\n"); goto out; } /* * Decode a word after the string to make sure we * end up positioned correctly after the string. */ if (w != 42) { printf("Fail: mbm_get_u0 w=%d\n", w); return; } if (strcmp(s, "one") != 0) { printf("Fail: mbm_get_u0 cmp: <%s>\n", s); return; } printf("Pass: mbm_get_u0\n"); out: MBC_FLUSH(&mbc); } /* * Parse a string that's NOT null terminated. */ static void mbm_get_u1() { uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; mbuf_chain_t mbc; char *s; int rc; uint16_t w; bzero(&mbc, sizeof (mbc)); MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); rc = smb_mbc_decodef(&mbc, "%6U..w", &test_sr, &s, &w); if (rc != 0) { printf("Fail: mbm_get_u1 decode\n"); goto out; } /* * Decode a word after the string to make sure we * end up positioned correctly after the string. */ if (w != 42) { printf("Fail: mbm_get_u1 w=%d\n", w); return; } if (strcmp(s, "one") != 0) { printf("Fail: mbm_get_u1 cmp: <%s>\n", s); return; } printf("Pass: mbm_get_u1\n"); out: MBC_FLUSH(&mbc); } /* parse exactly to end of data */ static void mbm_get_u2() { uint16_t wire[] = { 't', 'w', 'o' }; mbuf_chain_t mbc; char *s; int rc; bzero(&mbc, sizeof (mbc)); MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire)); rc = smb_mbc_decodef(&mbc, "%6U", &test_sr, &s); if (rc != 0) { printf("Fail: mbm_get_u2 decode\n"); goto out; } if (mbc.chain_offset != 6) { printf("Fail: mbm_get_u2 wrong pos\n"); return; } if (strcmp(s, "two") != 0) { printf("Fail: mbm_get_u2 cmp: <%s>\n", s); return; } printf("Pass: mbm_get_a2\n"); out: MBC_FLUSH(&mbc); } static void mbm_get_u3() { mbuf_chain_t mbc; char *s; int rc; bzero(&mbc, sizeof (mbc)); MBC_ATTACH_BUF(&mbc, (uchar_t *)wcsa, sizeof (wcsa)); rc = smb_mbc_decodef(&mbc, "%#U", &test_sr, sizeof (wcsa), &s); if (rc != 0) { printf("Fail: mbm_get_u3 decode\n"); goto out; } if (strcmp(s, mbsa) != 0) { printf("Fail: mbm_get_u3 cmp: <%s>\n", s); return; } printf("Pass: mbm_get_u3\n"); out: MBC_FLUSH(&mbc); } static void mbm_get_u4() { mbuf_chain_t mbc; char *s; int rc; bzero(&mbc, sizeof (mbc)); MBC_ATTACH_BUF(&mbc, (uchar_t *)wcsp, sizeof (wcsp)); rc = smb_mbc_decodef(&mbc, "%#U", &test_sr, sizeof (wcsp), &s); if (rc != 0) { printf("Fail: mbm_get_u4 decode\n"); goto out; } if (strcmp(s, mbsp) != 0) { printf("Fail: mbm_get_u4 cmp: <%s>\n", s); return; } printf("Pass: mbm_get_u4\n"); out: MBC_FLUSH(&mbc); } void test_mbmarshal() { smb_mbc_init(); test_ssn.dialect = 0x210; // SMB 2.1 test_sr.session = &test_ssn; test_sr.sr_magic = SMB_REQ_MAGIC; smb_srm_init(&test_sr); mbm_put_a0(); mbm_put_a1(); mbm_put_apad(); mbm_put_atrunc1(); mbm_put_atrunc2(); mbm_put_u0(); mbm_put_u1(); mbm_put_u3(); mbm_put_u4(); mbm_put_upad(); mbm_put_utrunc1(); mbm_put_utrunc2(); mbm_put_mbuf1(); mbm_put_mbuf2(); mbm_put_mbuf3(); mbm_get_a0(); mbm_get_a1(); mbm_get_a2(); mbm_get_u0(); mbm_get_u1(); mbm_get_u2(); mbm_get_u3(); mbm_get_u4(); smb_srm_fini(&test_sr); smb_mbc_fini(); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * A few excerpts from smb_kutil.c */ #include #include #include #include #include #include #include time_t tzh_leapcnt = 0; struct tm *smb_gmtime_r(time_t *clock, struct tm *result); time_t smb_timegm(struct tm *tm); struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; static const int days_in_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; uint64_t smb_time_unix_to_nt(timestruc_t *unix_time) { uint64_t nt_time; if ((unix_time->tv_sec == 0) && (unix_time->tv_nsec == 0)) return (0); nt_time = unix_time->tv_sec; nt_time *= 10000000; /* seconds to 100ns */ nt_time += unix_time->tv_nsec / 100; return (nt_time + NT_TIME_BIAS); } void smb_time_nt_to_unix(uint64_t nt_time, timestruc_t *unix_time) { uint32_t seconds; ASSERT(unix_time); if ((nt_time == 0) || (nt_time == -1)) { unix_time->tv_sec = 0; unix_time->tv_nsec = 0; return; } /* * Can't represent times less than or equal NT_TIME_BIAS, * so convert them to the oldest date we can store. * Note that time zero is "special" being converted * both directions as 0:0 (unix-to-nt, nt-to-unix). */ if (nt_time <= NT_TIME_BIAS) { unix_time->tv_sec = 0; unix_time->tv_nsec = 100; return; } nt_time -= NT_TIME_BIAS; seconds = nt_time / 10000000; unix_time->tv_sec = seconds; unix_time->tv_nsec = (nt_time % 10000000) * 100; } /* * smb_time_dos_to_unix * * Convert SMB_DATE & SMB_TIME values to a unix timestamp. * * A date/time field of 0 means that that server file system * assigned value need not be changed. The behaviour when the * date/time field is set to -1 is not documented but is * generally treated like 0. * If date or time is 0 or -1 the unix time is returned as 0 * so that the caller can identify and handle this special case. */ int32_t smb_time_dos_to_unix(int16_t date, int16_t time) { struct tm atm; if (((date == 0) || (time == 0)) || ((date == -1) || (time == -1))) { return (0); } atm.tm_year = ((date >> 9) & 0x3F) + 80; atm.tm_mon = ((date >> 5) & 0x0F) - 1; atm.tm_mday = ((date >> 0) & 0x1F); atm.tm_hour = ((time >> 11) & 0x1F); atm.tm_min = ((time >> 5) & 0x3F); atm.tm_sec = ((time >> 0) & 0x1F) << 1; return (smb_timegm(&atm)); } void smb_time_unix_to_dos(int32_t ux_time, int16_t *date_p, int16_t *time_p) { struct tm atm; int i; time_t tmp_time; if (ux_time == 0) { *date_p = 0; *time_p = 0; return; } tmp_time = (time_t)ux_time; (void) smb_gmtime_r(&tmp_time, &atm); if (date_p) { i = 0; i += atm.tm_year - 80; i <<= 4; i += atm.tm_mon + 1; i <<= 5; i += atm.tm_mday; *date_p = (short)i; } if (time_p) { i = 0; i += atm.tm_hour; i <<= 6; i += atm.tm_min; i <<= 5; i += atm.tm_sec >> 1; *time_p = (short)i; } } /* * smb_gmtime_r * * Thread-safe version of smb_gmtime. Returns a null pointer if either * input parameter is a null pointer. Otherwise returns a pointer * to result. * * Day of the week calculation: the Epoch was a thursday. * * There are no timezone corrections so tm_isdst and tm_gmtoff are * always zero, and the zone is always WET. */ struct tm * smb_gmtime_r(time_t *clock, struct tm *result) { time_t tsec; int year; int month; int sec_per_month; if (clock == 0 || result == 0) return (0); bzero(result, sizeof (struct tm)); tsec = *clock; tsec -= tzh_leapcnt; result->tm_wday = tsec / SECSPERDAY; result->tm_wday = (result->tm_wday + TM_THURSDAY) % DAYSPERWEEK; year = EPOCH_YEAR; while (tsec >= (isleap(year) ? (SECSPERDAY * DAYSPERLYEAR) : (SECSPERDAY * DAYSPERNYEAR))) { if (isleap(year)) tsec -= SECSPERDAY * DAYSPERLYEAR; else tsec -= SECSPERDAY * DAYSPERNYEAR; ++year; } result->tm_year = year - TM_YEAR_BASE; result->tm_yday = tsec / SECSPERDAY; for (month = TM_JANUARY; month <= TM_DECEMBER; ++month) { sec_per_month = days_in_month[month] * SECSPERDAY; if (month == TM_FEBRUARY && isleap(year)) sec_per_month += SECSPERDAY; if (tsec < sec_per_month) break; tsec -= sec_per_month; } result->tm_mon = month; result->tm_mday = (tsec / SECSPERDAY) + 1; tsec %= SECSPERDAY; result->tm_sec = tsec % 60; tsec /= 60; result->tm_min = tsec % 60; tsec /= 60; result->tm_hour = (int)tsec; return (result); } /* * smb_timegm * * Converts the broken-down time in tm to a time value, i.e. the number * of seconds since the Epoch (00:00:00 UTC, January 1, 1970). This is * not a POSIX or ANSI function. Per the man page, the input values of * tm_wday and tm_yday are ignored and, as the input data is assumed to * represent GMT, we force tm_isdst and tm_gmtoff to 0. * * Before returning the clock time, we use smb_gmtime_r to set up tm_wday * and tm_yday, and bring the other fields within normal range. I don't * think this is really how it should be done but it's convenient for * now. */ time_t smb_timegm(struct tm *tm) { time_t tsec; int dd; int mm; int yy; int year; if (tm == 0) return (-1); year = tm->tm_year + TM_YEAR_BASE; tsec = tzh_leapcnt; for (yy = EPOCH_YEAR; yy < year; ++yy) { if (isleap(yy)) tsec += SECSPERDAY * DAYSPERLYEAR; else tsec += SECSPERDAY * DAYSPERNYEAR; } for (mm = TM_JANUARY; mm < tm->tm_mon; ++mm) { dd = days_in_month[mm] * SECSPERDAY; if (mm == TM_FEBRUARY && isleap(year)) dd += SECSPERDAY; tsec += dd; } tsec += (tm->tm_mday - 1) * SECSPERDAY; tsec += tm->tm_sec; tsec += tm->tm_min * SECSPERMIN; tsec += tm->tm_hour * SECSPERHOUR; tm->tm_isdst = 0; (void) smb_gmtime_r(&tsec, tm); return (tsec); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. */ /* * Test putting/getting unicode strings in mbchains. */ #include #include #include #include #include #include #include #include "test_defs.h" static char mbsa[] = "A\xef\xbc\xa1."; // A fwA . (5) static char mbsp[] = "P\xf0\x9f\x92\xa9."; // P poop . (6) static smb_wchar_t wcsa[] = { 'A', 0xff21, '.', 0 }; // (3) static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4) /* * Put ASCII string with NULL */ static void msg_put_a0() { uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); rc = smb_msgbuf_encode(&mb, "sw", "one", 42); if (rc != 6) { printf("Fail: msg_put_a0 encode\n"); goto out; } if (memcmp(temp, wire, 6)) { printf("Fail: msg_put_a0 cmp:\n"); hexdump((uchar_t *)temp, 6); return; } printf("Pass: msg_put_a0\n"); out: smb_msgbuf_term(&mb); } /* * Put ASCII string, no NULL */ static void msg_put_a1() { uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); rc = smb_msgbuf_encode(&mb, "4sw", "one.", 42); if (rc != 6) { printf("Fail: msg_put_a1 encode\n"); goto out; } if (memcmp(temp, wire, 6)) { printf("Fail: msg_put_a1 cmp:\n"); hexdump((uchar_t *)temp, 6); return; } printf("Pass: msg_put_a1\n"); out: smb_msgbuf_term(&mb); } static void msg_put_apad() { uint8_t wire[] = { 'o', 'n', 'e', 0, 0 }; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); /* Encode with wire length > strlen */ rc = smb_msgbuf_encode(&mb, "5s", "one"); if (rc != 5) { printf("Fail: msg_put_apad encode\n"); goto out; } if (memcmp(temp, wire, 5)) { printf("Fail: msg_put_apad cmp:\n"); hexdump((uchar_t *)temp, 5); return; } printf("Pass: msg_put_apad\n"); out: smb_msgbuf_term(&mb); } static void msg_put_atrunc1() { uint8_t wire[] = { 'o', 'n', 'e', 't', }; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); /* Encode with wire length < strlen */ rc = smb_msgbuf_encode(&mb, "4s", "onetwo"); /* Trunc should put exactly 4 */ if (rc != 4) { printf("Fail: msg_put_atrunc1 encode\n"); goto out; } if (memcmp(temp, wire, 4)) { printf("Fail: msg_put_atrunc1 cmp:\n"); hexdump((uchar_t *)temp, 4); return; } printf("Pass: msg_put_atrunc1\n"); out: smb_msgbuf_term(&mb); } static void msg_put_atrunc2() { uint8_t wire[] = { 'o', 'n', 'e', 't', 0}; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; (void) memset(temp, 0, sizeof (temp)); smb_msgbuf_init(&mb, temp, 4, mbflags); /* Encode with wire length < strlen */ rc = smb_msgbuf_encode(&mb, "s", "onetwo"); /* Trunc should return "overflow" */ if (rc != -1) { printf("Fail: msg_put_atrunc2 encode rc=%d\n", rc); goto out; } if (memcmp(temp, wire, 5)) { printf("Fail: msg_put_atrunc2 cmp:\n"); hexdump((uchar_t *)temp, 5); return; } printf("Pass: msg_put_atrunc2\n"); out: smb_msgbuf_term(&mb); } /* * Put unicode string with NULL */ static void msg_put_u0() { uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); rc = smb_msgbuf_encode(&mb, "Uw", "one", 42); if (rc != 10) { printf("Fail: msg_put_u0 encode\n"); goto out; } if (memcmp(temp, wire, 10)) { printf("Fail: msg_put_u0 cmp:\n"); hexdump((uchar_t *)temp, 10); return; } printf("Pass: msg_put_u0\n"); out: smb_msgbuf_term(&mb); } /* * Put unicode string, no NULL */ static void msg_put_u1() { uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); rc = smb_msgbuf_encode(&mb, "8Uw", "one.", 42); if (rc != 10) { printf("Fail: msg_put_u1 encode\n"); goto out; } if (memcmp(temp, wire, 10)) { printf("Fail: msg_put_u1 cmp:\n"); hexdump((uchar_t *)temp, 10); return; } printf("Pass: msg_put_u1\n"); out: smb_msgbuf_term(&mb); } static void msg_put_u3() { uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); rc = smb_msgbuf_encode(&mb, "U", mbsa); if (rc != 8) { printf("Fail: msg_put_u3 encode\n"); goto out; } if (memcmp(temp, wcsa, 8)) { printf("Fail: msg_put_u3 cmp:\n"); hexdump((uchar_t *)temp, 8); return; } printf("Pass: msg_put_u3\n"); out: smb_msgbuf_term(&mb); } static void msg_put_u4() { uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); rc = smb_msgbuf_encode(&mb, "U", mbsp); if (rc != 10) { printf("Fail: msg_put_u4 encode\n"); goto out; } if (memcmp(temp, wcsp, 10)) { printf("Fail: msg_put_u4 cmp:\n"); hexdump((uchar_t *)temp, 10); return; } printf("Pass: msg_put_u4\n"); out: smb_msgbuf_term(&mb); } static void msg_put_upad() { uint16_t wire[] = { 'o', 'n', 'e', 0, 0 }; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); /* Encode with wire length > strlen */ rc = smb_msgbuf_encode(&mb, "10U", "one"); if (rc != 10) { printf("Fail: msg_put_upad encode\n"); goto out; } if (memcmp(temp, wire, 10)) { printf("Fail: msg_put_upad cmp:\n"); hexdump((uchar_t *)temp, 10); return; } printf("Pass: msg_put_upad\n"); out: smb_msgbuf_term(&mb); } static void msg_put_utrunc1() { uint16_t wire[] = { 'o', 'n', 'e', 't' }; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags); /* Encode with wire length < strlen */ rc = smb_msgbuf_encode(&mb, "8U", "onetwo"); /* Trunc should put exactly 8 */ if (rc != 8) { printf("Fail: msg_put_utrunc1 encode\n"); goto out; } if (memcmp(temp, wire, 8)) { printf("Fail: msg_put_utrunc1 cmp:\n"); hexdump((uchar_t *)temp, 8); return; } printf("Pass: msg_put_utrunc1\n"); out: smb_msgbuf_term(&mb); } static void msg_put_utrunc2() { uint16_t wire[] = { 'o', 'n', 'e', 't', 0 }; uint8_t temp[32]; smb_msgbuf_t mb; int mbflags = 0; int rc; (void) memset(temp, 0, sizeof (temp)); smb_msgbuf_init(&mb, temp, 8, mbflags); /* Encode with wire length < strlen */ rc = smb_msgbuf_encode(&mb, "U", "onetwo"); /* Trunc should return "overflow" */ if (rc != -1) { printf("Fail: msg_put_utrunc2 encode rc=%d\n", rc); goto out; } if (memcmp(temp, wire, 10)) { printf("Fail: msg_put_utrunc2 cmp:\n"); hexdump((uchar_t *)temp, 10); return; } printf("Pass: msg_put_utrunc2\n"); out: smb_msgbuf_term(&mb); } /* * Parse an ascii string. */ static void msg_get_a0() { uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; smb_msgbuf_t mb; int mbflags = 0; char *s; int rc; uint16_t w; smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags); rc = smb_msgbuf_decode(&mb, "sw", &s, &w); if (rc != 6) { printf("Fail: msg_get_a0 decode\n"); goto out; } /* * Decode a word after the string to make sure we * end up positioned correctly after the string. */ if (w != 42) { printf("Fail: msg_get_a0 w=%d\n", w); return; } if (strcmp(s, "one") != 0) { printf("Fail: msg_get_a0 cmp: <%s>\n", s); return; } printf("Pass: msg_get_a0\n"); out: smb_msgbuf_term(&mb); } /* * Parse an ascii string, no NULL */ static void msg_get_a1() { uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; smb_msgbuf_t mb; int mbflags = 0; char *s; int rc; uint16_t w; smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags); rc = smb_msgbuf_decode(&mb, "3s.w", &s, &w); if (rc != 6) { printf("Fail: msg_get_a1 decode\n"); goto out; } /* * Decode a word after the string to make sure we * end up positioned correctly after the string. */ if (w != 42) { printf("Fail: msg_get_a1 w=%d\n", w); return; } if (strcmp(s, "one") != 0) { printf("Fail: msg_get_a1 cmp: <%s>\n", s); return; } printf("Pass: msg_get_a1\n"); out: smb_msgbuf_term(&mb); } /* parse exactly to end of data */ static void msg_get_a2() { uint8_t wire[] = { 'o', 'n', 'e' }; smb_msgbuf_t mb; int mbflags = 0; char *s; int rc; smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags); rc = smb_msgbuf_decode(&mb, "3s", &s); if (rc != 3) { printf("Fail: msg_get_a2 decode\n"); goto out; } if (mb.scan != mb.end) { printf("Fail: msg_get_a2 wrong pos\n"); return; } if (strcmp(s, "one") != 0) { printf("Fail: msg_get_a2 cmp: <%s>\n", s); return; } printf("Pass: msg_get_a2\n"); out: smb_msgbuf_term(&mb); } /* * Parse a unicode string. */ static void msg_get_u0() { uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 }; smb_msgbuf_t mb; int mbflags = 0; char *s; int rc; uint16_t w; smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags); rc = smb_msgbuf_decode(&mb, "Uw", &s, &w); if (rc != 10) { printf("Fail: msg_get_u0 decode\n"); goto out; } /* * Decode a word after the string to make sure we * end up positioned correctly after the string. */ if (w != 42) { printf("Fail: msg_get_u0 w=%d\n", w); return; } if (strcmp(s, "one") != 0) { printf("Fail: msg_get_u0 cmp: <%s>\n", s); return; } printf("Pass: msg_get_u0\n"); out: smb_msgbuf_term(&mb); } /* * Parse a string that's NOT null terminated. */ static void msg_get_u1() { uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 }; smb_msgbuf_t mb; int mbflags = 0; char *s; int rc; uint16_t w; smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags); rc = smb_msgbuf_decode(&mb, "6U..w", &s, &w); if (rc != 10) { printf("Fail: msg_get_u1 decode\n"); goto out; } /* * Decode a word after the string to make sure we * end up positioned correctly after the string. */ if (w != 42) { printf("Fail: msg_get_u1 w=%d\n", w); return; } if (strcmp(s, "one") != 0) { printf("Fail: msg_get_u1 cmp: <%s>\n", s); return; } printf("Pass: msg_get_u1\n"); out: smb_msgbuf_term(&mb); } /* parse exactly to end of data */ static void msg_get_u2() { uint16_t wire[] = { 'o', 'n', 'e' }; smb_msgbuf_t mb; int mbflags = 0; char *s; int rc; smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags); rc = smb_msgbuf_decode(&mb, "6U", &s); if (rc != 6) { printf("Fail: msg_get_u2 decode\n"); goto out; } if (mb.scan != mb.end) { printf("Fail: msg_get_u2 wrong pos\n"); return; } if (strcmp(s, "one") != 0) { printf("Fail: msg_get_u2 cmp: <%s>\n", s); return; } printf("Pass: msg_get_u2\n"); out: smb_msgbuf_term(&mb); } static void msg_get_u3() { smb_msgbuf_t mb; int mbflags = 0; char *s; int rc; smb_msgbuf_init(&mb, (uint8_t *)wcsa, sizeof (wcsa), mbflags); rc = smb_msgbuf_decode(&mb, "#U", sizeof (wcsa), &s); if (rc != 8) { printf("Fail: msg_get_u3 decode\n"); goto out; } if (strcmp(s, mbsa) != 0) { printf("Fail: msg_get_u3 cmp: <%s>\n", s); return; } printf("Pass: msg_get_u3\n"); out: smb_msgbuf_term(&mb); } static void msg_get_u4() { smb_msgbuf_t mb; int mbflags = 0; char *s; int rc; smb_msgbuf_init(&mb, (uint8_t *)wcsp, sizeof (wcsp), mbflags); rc = smb_msgbuf_decode(&mb, "#U", sizeof (wcsp), &s); if (rc != 10) { printf("Fail: msg_get_u4 decode\n"); goto out; } if (strcmp(s, mbsp) != 0) { printf("Fail: msg_get_u4 cmp: <%s>\n", s); return; } printf("Pass: msg_get_u4\n"); out: smb_msgbuf_term(&mb); } void test_msgbuf() { msg_put_a0(); msg_put_a1(); msg_put_apad(); msg_put_atrunc1(); msg_put_atrunc2(); msg_put_u0(); msg_put_u1(); msg_put_u3(); msg_put_u4(); msg_put_upad(); msg_put_utrunc1(); msg_put_utrunc2(); msg_get_a0(); msg_get_a1(); msg_get_a2(); msg_get_u0(); msg_get_u1(); msg_get_u2(); msg_get_u3(); msg_get_u4(); } # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # set -o emacs # testoplock is always 32-bit setenv LD_LIBRARY_PATH ${ROOT}/usr/lib/smbsrv:${ROOT}/usr/lib:${ROOT}/lib echo 'Do one of: attach ${PID}' echo 'or: debug ${ROOT}/usr/lib/smbsrv/testoplock' # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2018 Nexenta Systems, Inc. All rights reserved. # PROG= testoplock OBJS_LOCAL= tol_main.o tol_misc.o OBJS_SMBSRV= smb_cmn_oplock.o OBJS_LIBSMB= smb_status_tbl.o OBJS= ${OBJS_LOCAL} ${OBJS_SMBSRV} ${OBJS_LIBSMB} SMBSRV_SRCDIR=../../../uts/common/fs/smbsrv SRCS= ${OBJS_LOCAL:.o=.c} \ ${OBJS_SMBSRV:%.o=${SMBSRV_SRCDIR}/%.c} include ../../Makefile.cmd include ../../Makefile.ctf # Note: need our sys includes _before_ ENVCPPFLAGS, proto etc. CPPFLAGS.first += -I. CPPFLAGS.first += -I../../../lib/libfakekernel/common CPPFLAGS.first += -I../../../lib/smbsrv/libfksmbsrv/common INCS += -I../../../uts/common CSTD= $(CSTD_GNU99) CFLAGS += $(CCVERBOSE) CFLAGS64 += $(CCVERBOSE) CPPFLAGS.master=$(DTEXTDOM) $(DTS_ERRNO) # CPPFLAGS is deliberatly set with a "=" and not a "+="... CPPFLAGS= $(CPPFLAGS.first) $(CPPFLAGS.master) CPPFLAGS += -D_REENTRANT CPPFLAGS += -DTESTJIG CPPFLAGS += -Dsyslog=smb_syslog CPPFLAGS += -D_LARGEFILE64_SOURCE=1 # Always debug here CPPFLAGS += -DDEBUG CPPFLAGS += $(INCS) LDFLAGS += $(ZNOLAZYLOAD) LDFLAGS += '-R$$ORIGIN/..' LDLIBS += -lfakekernel -lcmdutils LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 LINTFLAGS += -xerroff=E_NAME_USED_NOT_DEF2 LINTFLAGS += -xerroff=E_INCONS_ARG_DECL2 LINTFLAGS += -xerroff=E_INCONS_VAL_TYPE_DECL2 ROOTSMBDDIR = $(ROOTLIB)/smbsrv ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%) .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) $(POST_PROCESS) clean: -$(RM) $(OBJS) lint: # lint_SRCS include ../../Makefile.targ install: all $(ROOTSMBDFILE) tol_main.o : tol_main.c $(CC) $(CFLAGS) $(CPPFLAGS) -D_KMEMUSER -c tol_main.c $(POST_PROCESS_O) tol_misc.o : tol_misc.c $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \ -I../../../uts/common/smbsrv \ -I../../../common/smbsrv -c tol_misc.c $(POST_PROCESS_O) # OBJS_SMBSRV %.o: ../../../uts/common/fs/smbsrv/%.c $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \ -I../../../uts/common/smbsrv \ -I../../../common/smbsrv -c $< $(POST_PROCESS_O) # Hammerhead: Generate smb_status_tbl.h before compiling smb_status_tbl.c LIBSMB_SRCDIR = ../../../lib/smbsrv/libsmb/common SMB_STATUS_HDR = $(LIBSMB_SRCDIR)/smb_status_tbl.h smb_status_tbl.o: $(SMB_STATUS_HDR) $(SMB_STATUS_HDR): $(LIBSMB_SRCDIR)/smb_status_gen.awk $(SRC)/uts/common/smb/ntstatus.h $(AWK) -f $(LIBSMB_SRCDIR)/smb_status_gen.awk \ $(SRC)/uts/common/smb/ntstatus.h > $@ # OBJS_LIBSMB %.o: $(LIBSMB_SRCDIR)/%.c $(CC) $(CFLAGS) $(CPPFLAGS) -c $< $(POST_PROCESS_O) $(ROOTSMBDDIR)/%: % $(INS.file) #!/bin/sh # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # # Helper program to run fksmbd (user-space smbd for debugging) # using binaries from the proto area. [ -n "$ROOT" ] || { echo "Need a bldenv to set ROOT=..." exit 1; } # OK, setup env. to run it. LD_LIBRARY_PATH=$ROOT/usr/lib:$ROOT/lib export LD_LIBRARY_PATH # run with the passed options exec $ROOT/usr/lib/smbsrv/testoplock "$@" #!/bin/sh # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # # Helper program to run fksmbd (user-space smbd for debugging) # using binaries from the proto area. [ -n "$ROOT" ] || { echo "Need a bldenv to set ROOT=..." exit 1; } # OK, setup env. to run it. LD_LIBRARY_PATH=$ROOT/usr/lib:$ROOT/lib export LD_LIBRARY_PATH TOL=$ROOT/usr/lib/smbsrv/testoplock TESTS=${@:-case??.txt} # run the test cases for t in $TESTS do name=${t%.txt} $TOL < $name.txt > $name.tmp if diff -u $name.tmp $name.ref >/dev/null 2>&1 ; then echo "$name PASS" rm $name.tmp else echo "$name FAIL" diff -u $name.tmp $name.ref fi done open 1 open 1 OK req 1 req oplock fid=1 ret oplock=0x400 status=0x0 (SUCCESS) show ol_state=0x410 ( BATCH_OPLOCK EXCLUSIVE ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease= State=0x400 Excl=Y onlist: brk-open 2 *smb_oplock_ind_break fid=1 NewLevel=0x100, AckReq=1, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=1 NewLevel=0x100, OldLevel=0x400, AckReq=1) brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS) show ol_state=0x100410 ( BREAK_TO_TWO BATCH_OPLOCK EXCLUSIVE ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease= State=0x400 BreakTo=0x100 Excl=Y onlist: ack 1 ack: break fid=1, newstate=0x100, status=0x0 (SUCCESS) open 2 open 2 OK req 2 0x100 req oplock fid=2 ret oplock=0x100 status=0x0 (SUCCESS) show ol_state=0x100 ( LEVEL_TWO_OPLOCK ) Excl=n cnt_II=2 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease= State=0x100 Excl=N onlist: II fid=2 Lease= State=0x100 Excl=N onlist: II # Input for testoplock, case 01 open 1 req 1 show brk-open 2 show ack 1 open 2 req 2 0x100 show open 1 open 1 OK req 1 0x807 req oplock fid=1 ret oplock=0x807 status=0x0 (SUCCESS) show ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease= State=0x807 Excl=Y onlist: brk-open 2 *smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1) brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS) show ol_state=0x30017 ( BREAK_TO_HANDLE_CACHING BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease= State=0x807 BreakTo=0x803 Excl=Y onlist: ack 1 ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS) open 2 open 2 OK req 2 0x803 req oplock fid=2 ret oplock=0x803 status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=2 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease= State=0x803 Excl=N onlist: RH fid=2 Lease= State=0x803 Excl=N onlist: RH # Input for testoplock, case 02 open 1 req 1 0x807 show brk-open 2 show ack 1 open 2 req 2 0x803 show open 1 3 open 1 OK req 1 0x803 req oplock fid=1 ret oplock=0x803 status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=3 State=0x803 Excl=N onlist: RH open 2 3 open 2 OK req 2 0x803 *smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE) req oplock fid=2 ret oplock=0x803 status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=3 State=0x0 Excl=N onlist: fid=2 Lease=3 State=0x803 Excl=N onlist: RH # Input for testoplock, case 03 open 1 3 req 1 0x803 show open 2 3 req 2 0x803 show open 1 3 open 1 OK req 1 0x801 req oplock fid=1 ret oplock=0x801 status=0x0 (SUCCESS) show ol_state=0x1 ( READ_CACHING ) Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=3 State=0x801 Excl=N onlist: R open 2 3 open 2 OK req 2 0x801 *smb_oplock_ind_break fid=1 NewLevel=0x1, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE) req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS) show ol_state=0x1 ( READ_CACHING ) Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=3 State=0x0 Excl=N onlist: fid=2 Lease=3 State=0x801 Excl=N onlist: R # Input for testoplock, case 04 open 1 3 req 1 0x801 show open 2 3 req 2 0x801 show open 2 4 open 2 OK req 2 0x803 req oplock fid=2 ret oplock=0x803 status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=1 fid=2 Lease=4 State=0x803 Excl=N onlist: RH open 3 4 open 3 OK req 3 0x803 *smb_oplock_ind_break fid=2 NewLevel=0x3, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE) req oplock fid=3 ret oplock=0x803 status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=2 fid=2 Lease=4 State=0x0 Excl=N onlist: fid=3 Lease=4 State=0x803 Excl=N onlist: RH open 1 open 1 OK brk-write 1 *smb_oplock_ind_break fid=3 NewLevel=0x0, AckReq=1, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=3 NewLevel=0x800, OldLevel=0x803, AckReq=1) brk-write 1 ret status=0x0 (SUCCESS) show ol_state=0x80003 ( BREAK_TO_NO_CACHING HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=1 ofile_cnt=3 fid=2 Lease=4 State=0x0 Excl=N onlist: fid=3 Lease=4 State=0x803 BreakTo=0x800 Excl=N onlist: RHBQ(to none) fid=1 Lease= State=0x0 Excl=N onlist: ack 3 ack: break fid=3, newstate=0x800, status=0x0 (SUCCESS) show ol_state=0x10000000 ( NO_OPLOCK ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=3 fid=2 Lease=4 State=0x0 Excl=N onlist: fid=3 Lease=4 State=0x800 Excl=N onlist: fid=1 Lease= State=0x0 Excl=N onlist: open 4 4 open 4 OK req 4 0x803 req oplock fid=4 ret oplock=0x803 status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=4 fid=2 Lease=4 State=0x0 Excl=N onlist: fid=3 Lease=4 State=0x800 Excl=N onlist: fid=1 Lease= State=0x0 Excl=N onlist: fid=4 Lease=4 State=0x803 Excl=N onlist: RH close 4 *smb_oplock_ind_break fid=4 NewLevel=0x0, AckReq=0, ComplStatus=0x216 (OPLOCK_HANDLE_CLOSED) close OK req 3 0x803 req oplock fid=3 ret oplock=0x803 status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=3 fid=2 Lease=4 State=0x0 Excl=N onlist: fid=3 Lease=4 State=0x803 Excl=N onlist: RH fid=1 Lease= State=0x0 Excl=N onlist: # Input for testoplock, case 05 open 2 4 req 2 0x803 show open 3 4 req 3 0x803 show open 1 brk-write 1 show ack 3 show open 4 4 req 4 0x803 show close 4 req 3 0x803 show open 1 1 open 1 OK req 1 0x801 req oplock fid=1 ret oplock=0x801 status=0x0 (SUCCESS) open 2 2 open 2 OK req 2 0x801 req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS) show ol_state=0x1 ( READ_CACHING ) Excl=n cnt_II=0 cnt_R=2 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x801 Excl=N onlist: R fid=2 Lease=2 State=0x801 Excl=N onlist: R brk-write 1 *smb_oplock_ind_break fid=2 NewLevel=0x0, AckReq=0, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=2 NewLevel=0x800, OldLevel=0x801, AckReq=0) brk-write 1 ret status=0x0 (SUCCESS) show ol_state=0x1 ( READ_CACHING ) Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x801 Excl=N onlist: R fid=2 Lease=2 State=0x800 Excl=N onlist: open 3 2 open 3 OK req 3 0x801 req oplock fid=3 ret oplock=0x801 status=0x0 (SUCCESS) close 3 *smb_oplock_ind_break fid=3 NewLevel=0x0, AckReq=0, ComplStatus=0x216 (OPLOCK_HANDLE_CLOSED) close OK req 2 0x801 req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS) show ol_state=0x1 ( READ_CACHING ) Excl=n cnt_II=0 cnt_R=2 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x801 Excl=N onlist: R fid=2 Lease=2 State=0x801 Excl=N onlist: R brk-write 2 *smb_oplock_ind_break fid=1 NewLevel=0x0, AckReq=0, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=1 NewLevel=0x800, OldLevel=0x801, AckReq=0) brk-write 2 ret status=0x0 (SUCCESS) show ol_state=0x1 ( READ_CACHING ) Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x800 Excl=N onlist: fid=2 Lease=2 State=0x801 Excl=N onlist: R brk-write 1 *smb_oplock_ind_break fid=2 NewLevel=0x0, AckReq=0, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=2 NewLevel=0x800, OldLevel=0x801, AckReq=0) brk-write 1 ret status=0x0 (SUCCESS) show ol_state=0x10000000 ( NO_OPLOCK ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x800 Excl=N onlist: fid=2 Lease=2 State=0x800 Excl=N onlist: # Input for testoplock, case 06 # Modeled after smbtorture smb2.lease.nobreakself open 1 1 req 1 0x801 open 2 2 req 2 0x801 # both 1,2 should have R show # write 1 should leave 1:R 2:none brk-write 1 show # upgrade 2 back to R open 3 2 req 3 0x801 close 3 # ind_break will "move" the lease to h2 (1:R 2:R) req 2 0x801 show # write 2 should leave 1:none 2:R brk-write 2 show # write 1 should leave 1:none 2:none brk-write 1 show open 1 3 open 1 OK req 1 0x803 req oplock fid=1 ret oplock=0x803 status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=3 State=0x803 Excl=N onlist: RH open 2 3 open 2 OK req 2 0x807 *smb_oplock_ind_break fid=1 NewLevel=0x7, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE) req oplock fid=2 ret oplock=0x807 status=0x0 (SUCCESS) show ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=2) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=3 State=0x0 Excl=N onlist: fid=2 Lease=3 State=0x807 Excl=Y onlist: # Input for testoplock, case 07 # Modeled after smbtorture smb2.lease.upgrade open 1 3 req 1 0x803 show open 2 3 req 2 0x807 show open 1 1 open 1 OK req 1 0x805 req oplock fid=1 ret oplock=0x805 status=0x0 (SUCCESS) show ol_state=0x15 ( EXCLUSIVE WRITE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=1 State=0x805 Excl=Y onlist: open 2 2 open 2 OK brk-open 2 *smb_oplock_ind_break fid=1 NewLevel=0x1, AckReq=1, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=1 NewLevel=0x801, OldLevel=0x805, AckReq=1) brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS) show ol_state=0x10015 ( BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x805 BreakTo=0x801 Excl=Y onlist: fid=2 Lease=2 State=0x0 Excl=N onlist: ack 1 ack: break fid=1, newstate=0x801, status=0x0 (SUCCESS) show ol_state=0x1 ( READ_CACHING ) Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x801 Excl=N onlist: R fid=2 Lease=2 State=0x0 Excl=N onlist: req 2 0x801 req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS) show ol_state=0x1 ( READ_CACHING ) Excl=n cnt_II=0 cnt_R=2 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x801 Excl=N onlist: R fid=2 Lease=2 State=0x801 Excl=N onlist: R # Input for testoplock, case 08 # Modeled after smbtorture smb2.lease.upgrade3 # sub-case: "R" "RH" "RW" "R" open 1 1 req 1 0x805 show open 2 2 brk-open 2 show ack 1 show req 2 0x801 show open 1 open 1 OK req 1 0x100 req oplock fid=1 ret oplock=0x100 status=0x0 (SUCCESS) show ol_state=0x100 ( LEVEL_TWO_OPLOCK ) Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease= State=0x100 Excl=N onlist: II open 2 2 open 2 OK brk-open 2 brk-open 2 ret status=0x0 (SUCCESS) show ol_state=0x100 ( LEVEL_TWO_OPLOCK ) Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease= State=0x100 Excl=N onlist: II fid=2 Lease=2 State=0x0 Excl=N onlist: req 2 0x803 req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED) show ol_state=0x100 ( LEVEL_TWO_OPLOCK ) Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease= State=0x100 Excl=N onlist: II fid=2 Lease=2 State=0x0 Excl=N onlist: # Input for testoplock, case 09 # Modeled after smbtorture smb2.lease.oplock open 1 req 1 0x100 show open 2 2 brk-open 2 show req 2 0x803 show open 1 open 1 OK req 1 0x100 req oplock fid=1 ret oplock=0x100 status=0x0 (SUCCESS) show ol_state=0x100 ( LEVEL_TWO_OPLOCK ) Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease= State=0x100 Excl=N onlist: II open 2 2 open 2 OK brk-open 2 brk-open 2 ret status=0x0 (SUCCESS) show ol_state=0x100 ( LEVEL_TWO_OPLOCK ) Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease= State=0x100 Excl=N onlist: II fid=2 Lease=2 State=0x0 Excl=N onlist: req 2 0x807 req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED) req 2 0x803 req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED) req 2 0x801 req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS) show ol_state=0x101 ( LEVEL_TWO_OPLOCK READ_CACHING ) Excl=n cnt_II=1 cnt_R=1 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease= State=0x100 Excl=N onlist: II fid=2 Lease=2 State=0x801 Excl=N onlist: R # Input for testoplock, case 10 # Modeled after smbtorture smb2.lease.oplock open 1 req 1 0x100 show open 2 2 brk-open 2 show req 2 0x807 req 2 0x803 req 2 0x801 show open 1 1 open 1 OK req 1 0x807 req oplock fid=1 ret oplock=0x807 status=0x0 (SUCCESS) show ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=1 State=0x807 Excl=Y onlist: open 2 open 2 OK brk-open 2 *smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1) brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS) ack 1 ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x803 Excl=N onlist: RH fid=2 Lease= State=0x0 Excl=N onlist: req 2 0x100 req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x803 Excl=N onlist: RH fid=2 Lease= State=0x0 Excl=N onlist: # Input for testoplock, case 11 # Modeled after smbtorture smb2.lease.break2 open 1 1 req 1 0x807 show open 2 brk-open 2 ack 1 show req 2 0x100 show open 1 1 open 1 OK req 1 0x807 req oplock fid=1 ret oplock=0x807 status=0x0 (SUCCESS) show ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=1 State=0x807 Excl=Y onlist: open 2 open 2 OK brk-open 2 *smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1) brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS) waiters 2 1 waiters 0 -> 1 show ol_state=0x30017 ( BREAK_TO_HANDLE_CACHING BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x807 BreakTo=0x803 Excl=Y onlist: fid=2 Lease= State=0x0 Excl=N onlist: open 3 open 3 OK brk-open 3 4 brk-open 3 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS) waiters 3 2 waiters 1 -> 2 show ol_state=0x80017 ( BREAK_TO_NO_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=3 fid=1 Lease=1 State=0x807 BreakTo=0x803 Excl=Y onlist: fid=2 Lease= State=0x0 Excl=N onlist: fid=3 Lease= State=0x0 Excl=N onlist: ack 1 0x803 ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS) *smb_oplock_ind_break_in_ack fid=1 NewLevel=0x1, AckReq=1 *smb_oplock_send_break fid=1 NewLevel=0x801, OldLevel=0x803, AckReq=1) show ol_state=0x80003 ( BREAK_TO_NO_CACHING HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=1 ofile_cnt=3 fid=1 Lease=1 State=0x803 BreakTo=0x801 Excl=N onlist: RHBQ(to none) fid=2 Lease= State=0x0 Excl=N onlist: fid=3 Lease= State=0x0 Excl=N onlist: ack 1 0x801 ack: break fid=1, newstate=0x801, status=0x0 (SUCCESS) *smb_oplock_ind_break_in_ack fid=1 NewLevel=0x0, AckReq=0 *smb_oplock_send_break fid=1 NewLevel=0x800, OldLevel=0x801, AckReq=0) show ol_state=0x10000000 ( NO_OPLOCK ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=3 fid=1 Lease=1 State=0x800 Excl=N onlist: fid=2 Lease= State=0x0 Excl=N onlist: fid=3 Lease= State=0x0 Excl=N onlist: # Input for testoplock, case 12 # simulate smbtorture smb2.lease.breaking3 # open 1 1 req 1 0x807 show # # a conflicting open (no oplock) is blocked until lease break ack open 2 brk-open 2 waiters 2 1 show # should see lease break RWH to RH, and brk-open would block. # now a conflicting open with disp=overwrite(4), no oplock open 3 brk-open 3 4 waiters 3 2 show # should see break_to_none pending (but no break ind yet) # and brk-open shoud block (break in progress) # # ack the first lease break above (RWH to RH) # should get a new break ind. (RH to R) ar=1 ack 1 0x803 show # ack the second lease break (RH to R) # should get a new break ind. (R to none) ar=0 ack 1 0x801 show open 1 1 open 1 OK req 1 0x801 req oplock fid=1 ret oplock=0x801 status=0x0 (SUCCESS) show ol_state=0x1 ( READ_CACHING ) Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=1 State=0x801 Excl=N onlist: R open 2 1 open 2 OK brk-open 2 brk-open 2 ret status=0x0 (SUCCESS) req 2 0x807 *smb_oplock_ind_break fid=1 NewLevel=0x7, AckReq=0, ComplStatus=0x215 (OPLOCK_SWITCHED_TO_NEW_HANDLE) req oplock fid=2 ret oplock=0x807 status=0x0 (SUCCESS) show ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=2) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x0 Excl=N onlist: fid=2 Lease=1 State=0x807 Excl=Y onlist: move 2 1 move 2 1 show ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x807 Excl=Y onlist: fid=2 Lease=1 State=0x0 Excl=N onlist: close 2 close OK show ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=1 State=0x807 Excl=Y onlist: open 3 2 open 3 OK brk-open 3 *smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1) brk-open 3 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS) ack 1 ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x803 Excl=N onlist: RH fid=3 Lease=2 State=0x0 Excl=N onlist: req 3 0x801 req oplock fid=3 ret oplock=0x801 status=0x0 (SUCCESS) show ol_state=0x23 ( MIXED_R_AND_RH HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=1 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x803 Excl=N onlist: RH fid=3 Lease=2 State=0x801 Excl=N onlist: R # Input for testoplock, case 13 # simulate smbtorture smb2.lease.complex1 # open 1 1 req 1 0x801 show # upgrade lease 1 open 2 1 brk-open 2 req 2 0x807 show move 2 1 show close 2 show # contend via lease2 open 3 2 brk-open 3 ack 1 show req 3 0x801 show open 1 1 open 1 OK req 1 0x807 req oplock fid=1 ret oplock=0x807 status=0x0 (SUCCESS) show ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=1 State=0x807 Excl=Y onlist: brk-setinfo 1 0xa brk-setinfo 1 0xa ret status=0x0 (SUCCESS) show ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=1 fid=1 Lease=1 State=0x807 Excl=Y onlist: open 2 2 open 2 OK brk-open 2 *smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1) brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS) show ol_state=0x30017 ( BREAK_TO_HANDLE_CACHING BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING ) Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x807 BreakTo=0x803 Excl=Y onlist: fid=2 Lease=2 State=0x0 Excl=N onlist: ack 1 0x803 ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x803 Excl=N onlist: RH fid=2 Lease=2 State=0x0 Excl=N onlist: req 2 0x807 req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED) req 2 0x803 req oplock fid=2 ret oplock=0x803 status=0x0 (SUCCESS) brk-setinfo 1 0xa *smb_oplock_ind_break fid=2 NewLevel=0x1, AckReq=1, ComplStatus=0x0 (SUCCESS) *smb_oplock_send_break fid=2 NewLevel=0x801, OldLevel=0x803, AckReq=1) brk-setinfo 1 0xa ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS) show ol_state=0x3 ( HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=1 ofile_cnt=2 fid=1 Lease=1 State=0x803 Excl=N onlist: RH fid=2 Lease=2 State=0x803 BreakTo=0x801 Excl=N onlist: RHBQ(to read) ack 2 0x801 ack: break fid=2, newstate=0x801, status=0x0 (SUCCESS) show ol_state=0x23 ( MIXED_R_AND_RH HANDLE_CACHING READ_CACHING ) Excl=n cnt_II=0 cnt_R=1 cnt_RH=1 cnt_RHBQ=0 ofile_cnt=2 fid=1 Lease=1 State=0x803 Excl=N onlist: RH fid=2 Lease=2 State=0x801 Excl=N onlist: R # Input for testoplock, case 13 # simulate smbtorture smb2.lease.v2_rename # Create (open_if, lease=e0dd(1), RWH, epoch=0x4711 ) # resp: fid=1, RWH, epoch=0x4712 # Expect no breaks, one handle, state=RWH open 1 1 req 1 0x807 show # SetInfo fid=1, newname=...dst.dat # resp OK (no breaks) brk-setinfo 1 0xa show # Create, open_if, lease=e0dd(1), RWH, epoch=0x4712 # resp fid=5 RWH, flags=0 (not breaking) # Close fid=5 / resp # This is handled without calling the common oplock layer, # by logic at the top of smb2_lease_acquire # Create, open_if, lease=feed(2), RWH, epoch=0x0044 # (resp pending, will break RWH to RH) open 2 2 brk-open 2 show # Lease Break Notify, lease=e0dd(1), RWH to RH, epoch=0x4713 # Lease Break Ack, lease=e0dd, RH # resp (same) ack 1 0x803 show # Create-resp, fid=9 lease=feed(2), state=RH, epoch=0x0045 # Now that open 2 breaking is done, it can request. # Will fail RWH, succeed RH req 2 0x807 req 2 0x803 # SetInfo fid=1, newname=...src.dat # (resp blocked -- win10 does not go pending? we do) # Should get rid of handle caching (RH to R) brk-setinfo 1 0xa show # Lease Break Notify, lease=feed(2), RH to R, epoch=0x0046 # Lease Break Ack, lease=feed(2), state=R # Break Ack Resp (same) ack 2 0x801 show # SetInfo resp, fid=1 (note: races with Break Ack resp.) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Tintri by DDN, Inc. All rights reserved. * Copyright 2022 RackTop Systems, Inc. */ /* * Function prototypes needed by the "testoplock" program * (a small subset of what the SMB server uses) */ #ifndef _SMB_KPROTO_H_ #define _SMB_KPROTO_H_ #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include boolean_t smb_ofile_is_open(smb_ofile_t *); boolean_t smb_node_is_file(smb_node_t *); /* * SMB locked list function prototypes */ void smb_llist_init(void); void smb_llist_fini(void); void smb_llist_constructor(smb_llist_t *, size_t, size_t); void smb_llist_destructor(smb_llist_t *); void smb_llist_enter(smb_llist_t *ll, krw_t); void smb_llist_exit(smb_llist_t *); void smb_llist_post(smb_llist_t *, void *, smb_dtorproc_t); void smb_llist_flush(smb_llist_t *); void smb_llist_insert_head(smb_llist_t *ll, void *obj); void smb_llist_insert_tail(smb_llist_t *ll, void *obj); void smb_llist_remove(smb_llist_t *ll, void *obj); int smb_llist_upgrade(smb_llist_t *ll); uint32_t smb_llist_get_count(smb_llist_t *ll); #define smb_llist_head(ll) list_head(&(ll)->ll_list) #define smb_llist_next(ll, obj) list_next(&(ll)->ll_list, obj) int smb_account_connected(smb_user_t *user); /* * Common oplock functions */ uint32_t smb_oplock_request(smb_request_t *, smb_ofile_t *, uint32_t *); uint32_t smb_oplock_request_LH(smb_request_t *, smb_ofile_t *, uint32_t *); uint32_t smb_oplock_ack_break(smb_request_t *, smb_ofile_t *, uint32_t *); uint32_t smb_oplock_break_PARENT(smb_node_t *, smb_ofile_t *); uint32_t smb_oplock_break_OPEN(smb_node_t *, smb_ofile_t *, uint32_t DesiredAccess, uint32_t CreateDisposition); uint32_t smb_oplock_break_BATCH(smb_node_t *, smb_ofile_t *, uint32_t DesiredAccess, uint32_t CreateDisposition); uint32_t smb_oplock_break_HANDLE(smb_node_t *, smb_ofile_t *); void smb_oplock_break_CLOSE(smb_node_t *, smb_ofile_t *); uint32_t smb_oplock_break_READ(smb_node_t *, smb_ofile_t *); uint32_t smb_oplock_break_WRITE(smb_node_t *, smb_ofile_t *); uint32_t smb_oplock_break_SETINFO(smb_node_t *, smb_ofile_t *ofile, uint32_t InfoClass); uint32_t smb_oplock_break_DELETE(smb_node_t *, smb_ofile_t *); void smb_oplock_close(smb_ofile_t *); void smb_oplock_ind_break(smb_ofile_t *, uint32_t, boolean_t, uint32_t); void smb_oplock_ind_break_in_ack(smb_request_t *, smb_ofile_t *, uint32_t, boolean_t); void smb_oplock_send_break(smb_request_t *); uint32_t smb_oplock_wait_ack(smb_request_t *, uint32_t); uint32_t smb_oplock_wait_break(smb_request_t *, smb_node_t *, int); uint32_t smb_oplock_wait_break_fem(smb_node_t *, int); void smb_oplock_move(smb_node_t *, smb_ofile_t *, smb_ofile_t *); /* * Protocol-specific oplock functions * (not needed here) */ int smb_lock_range_access(smb_request_t *, smb_node_t *, uint64_t, uint64_t, boolean_t); int smb_fem_oplock_install(smb_node_t *); void smb_fem_oplock_uninstall(smb_node_t *); #ifdef __cplusplus } #endif #endif /* _SMB_KPROTO_H_ */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Nexenta by DDN, Inc. All rights reserved. * Copyright 2022 RackTop Systems, Inc. */ /* * Structures and type definitions needed by the "testoplock" program * (a small subset of what the SMB server uses) */ #ifndef _SMB_KTYPES_H #define _SMB_KTYPES_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include typedef struct smb_session smb_session_t; typedef struct smb_user smb_user_t; typedef struct smb_tree smb_tree_t; /* * Destructor object used in the locked-list delete queue. */ #define SMB_DTOR_MAGIC 0x44544F52 /* DTOR */ #define SMB_DTOR_VALID(d) \ ASSERT(((d) != NULL) && ((d)->dt_magic == SMB_DTOR_MAGIC)) typedef void (*smb_dtorproc_t)(void *); typedef struct smb_dtor { list_node_t dt_lnd; uint32_t dt_magic; void *dt_object; smb_dtorproc_t dt_proc; } smb_dtor_t; typedef struct smb_llist { krwlock_t ll_lock; list_t ll_list; uint32_t ll_count; uint64_t ll_wrop; kmutex_t ll_mutex; list_t ll_deleteq; uint32_t ll_deleteq_count; boolean_t ll_flushing; } smb_llist_t; /* * Per smb_node oplock state */ typedef struct smb_oplock { kmutex_t ol_mutex; boolean_t ol_fem; /* fem monitor installed? */ struct smb_ofile *excl_open; uint32_t ol_state; int32_t cnt_II; int32_t cnt_R; int32_t cnt_RH; int32_t cnt_RHBQ; int32_t waiters; kcondvar_t WaitingOpenCV; } smb_oplock_t; /* * Per smb_ofile oplock state */ typedef struct smb_oplock_grant { /* smb protocol-level state */ uint32_t og_state; /* what client has now */ uint32_t og_breakto; /* level breaking to */ boolean_t og_breaking; uint16_t og_dialect; /* how to send breaks */ kcondvar_t og_ack_cv; /* Wait for ACK */ /* File-system level state */ uint8_t onlist_II; uint8_t onlist_R; uint8_t onlist_RH; uint8_t onlist_RHBQ; uint8_t BreakingToRead; } smb_oplock_grant_t; #define SMB_LEASE_KEY_SZ 16 struct smb_lease; #define SMB_NODE_MAGIC 0x4E4F4445 /* 'NODE' */ #define SMB_NODE_VALID(p) ASSERT((p)->n_magic == SMB_NODE_MAGIC) typedef enum { SMB_NODE_STATE_AVAILABLE = 0, SMB_NODE_STATE_DESTROYING } smb_node_state_t; /* * waiting_event # of clients requesting FCN * n_timestamps cached timestamps * n_allocsz cached file allocation size * n_dnode directory node * n_unode unnamed stream node * delete_on_close_cred credentials for delayed delete */ typedef struct smb_node { list_node_t n_lnd; uint32_t n_magic; krwlock_t n_lock; kmutex_t n_mutex; smb_node_state_t n_state; uint32_t n_refcnt; uint32_t n_open_count; volatile int flags; smb_llist_t n_ofile_list; smb_oplock_t n_oplock; } smb_node_t; #define NODE_FLAGS_WRITE_THROUGH 0x00100000 #define NODE_FLAGS_DELETE_COMMITTED 0x20000000 #define NODE_FLAGS_DELETE_ON_CLOSE 0x40000000 /* * Some flags for ofile structure * * SMB_OFLAGS_SET_DELETE_ON_CLOSE * Set this flag when the corresponding open operation whose * DELETE_ON_CLOSE bit of the CreateOptions is set. If any * open file instance has this bit set, the NODE_FLAGS_DELETE_ON_CLOSE * will be set for the file node upon close. */ /* SMB_OFLAGS_READONLY 0x0001 (obsolete) */ #define SMB_OFLAGS_EXECONLY 0x0002 #define SMB_OFLAGS_SET_DELETE_ON_CLOSE 0x0004 #define SMB_OFLAGS_LLF_POS_VALID 0x0008 #define SMB_OFILE_MAGIC 0x4F464C45 /* 'OFLE' */ #define SMB_OFILE_VALID(p) \ ASSERT((p != NULL) && ((p)->f_magic == SMB_OFILE_MAGIC)) /* * This is the size of the per-handle "Lock Sequence" array. * See LockSequenceIndex in [MS-SMB2] 2.2.26, and smb2_lock.c */ #define SMB_OFILE_LSEQ_MAX 64 /* {arg_open,ofile}->dh_vers values */ typedef enum { SMB2_NOT_DURABLE = 0, SMB2_DURABLE_V1, SMB2_DURABLE_V2, SMB2_RESILIENT, } smb_dh_vers_t; /* * See the long "Ofile State Machine" comment in smb_ofile.c */ typedef enum { SMB_OFILE_STATE_ALLOC = 0, SMB_OFILE_STATE_OPEN, SMB_OFILE_STATE_SAVE_DH, SMB_OFILE_STATE_SAVING, SMB_OFILE_STATE_CLOSING, SMB_OFILE_STATE_CLOSED, SMB_OFILE_STATE_ORPHANED, SMB_OFILE_STATE_RECONNECT, SMB_OFILE_STATE_EXPIRED, SMB_OFILE_STATE_SENTINEL } smb_ofile_state_t; typedef struct smb_ofile { list_node_t f_tree_lnd; /* t_ofile_list */ list_node_t f_node_lnd; /* n_ofile_list */ list_node_t f_dh_lnd; /* sv_persistid_ht */ uint32_t f_magic; kmutex_t f_mutex; smb_ofile_state_t f_state; uint16_t f_fid; uint16_t f_ftype; uint32_t f_refcnt; uint32_t f_granted_access; uint32_t f_share_access; smb_node_t *f_node; smb_oplock_grant_t f_oplock; boolean_t f_oplock_closing; uint8_t TargetOplockKey[SMB_LEASE_KEY_SZ]; uint8_t ParentOplockKey[SMB_LEASE_KEY_SZ]; struct smb_lease *f_lease; } smb_ofile_t; typedef struct smb_request { list_node_t sr_session_lnd; uint32_t sr_magic; kmutex_t sr_mutex; } smb_request_t; #ifdef __cplusplus } #endif #endif /* _SMB_KTYPES_H */ #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * User-level dtrace for testoplock * Usage: dtrace -s tol.d -c ./testoplock */ #pragma D option flowindent self int trace; self int mask; /* * Trace almost everything */ pid$target:testoplock::entry { self->trace++; } /* * If traced and not masked, print entry/return */ pid$target:testoplock::entry /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg0); printf("\t0x%x", arg1); printf("\t0x%x", arg2); printf("\t0x%x", arg3); printf("\t0x%x", arg4); printf("\t0x%x", arg5); } /* Skip the bsearch calls. */ pid$target:testoplock:xlate_nt_status:entry { self->mask++; } pid$target:testoplock:xlate_nt_status:return { self->mask--; } pid$target:testoplock::return /self->trace > 0 && self->mask == 0/ { printf("\t0x%x", arg1); } pid$target:testoplock::return { self->trace--; } /* ---------------------- */ pid$target::smb_oplock_request:entry { self->sr = arg0; self->of = arg1; self->statep = arg2; this->state = *(uint32_t *)copyin(self->statep, 4); printf(" entry state=0x%x\n", this->state); } pid$target::smb_oplock_request:return { this->sr = (userland pid`smb_request_t *)self->sr; this->state = *(uint32_t *)copyin(self->statep, 4); printf(" return state=0x%x\n", this->state); printf("\nsr->arg.open = "); print(this->sr->arg.open); } pid$target::smb_oplock_break_cmn:entry { this->node = (userland pid`smb_node_t *)arg0; this->ofile = (userland pid`smb_ofile_t *)arg1; printf("\nnode->n_oplock = "); print(this->node->n_oplock); printf("\nofile->f_oplock = "); print(this->ofile->f_oplock); } pid$target::smb_oplock_ind_break:entry { this->ofile = (userland pid`smb_ofile_t *)arg0; printf("\nofile->f_oplock = "); print(this->ofile->f_oplock); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2020 Tintri by DDN, Inc. All rights reserved. * Copyright 2019 Joyent, Inc. * Copyright 2022 RackTop Systems, Inc. */ /* * Test & debug program for oplocks * * This implements a simple command reader which accepts * commands to simulate oplock events, and prints the * state changes and actions that would happen after * each event. */ #include #include #include #include #include #include #include #include #include #include extern const char *xlate_nt_status(uint32_t); #define OPLOCK_CACHE_RWH (READ_CACHING | HANDLE_CACHING | WRITE_CACHING) #define OPLOCK_TYPE (LEVEL_TWO_OPLOCK | LEVEL_ONE_OPLOCK |\ BATCH_OPLOCK | OPLOCK_LEVEL_GRANULAR) #define MAXFID 10 smb_node_t root_node, test_node; smb_ofile_t ofile_array[MAXFID]; smb_request_t test_sr; uint32_t last_ind_break_level; char cmdbuf[100]; static void run_ind_break_in_ack(smb_ofile_t *); #define BIT_DEF(name) { name, #name } struct bit_defs { uint32_t mask; const char *name; } state_bits[] = { BIT_DEF(NO_OPLOCK), BIT_DEF(BREAK_TO_NO_CACHING), BIT_DEF(BREAK_TO_WRITE_CACHING), BIT_DEF(BREAK_TO_HANDLE_CACHING), BIT_DEF(BREAK_TO_READ_CACHING), BIT_DEF(BREAK_TO_TWO_TO_NONE), BIT_DEF(BREAK_TO_NONE), BIT_DEF(BREAK_TO_TWO), BIT_DEF(BATCH_OPLOCK), BIT_DEF(LEVEL_ONE_OPLOCK), BIT_DEF(LEVEL_TWO_OPLOCK), BIT_DEF(MIXED_R_AND_RH), BIT_DEF(EXCLUSIVE), BIT_DEF(WRITE_CACHING), BIT_DEF(HANDLE_CACHING), BIT_DEF(READ_CACHING), { 0, NULL } }; /* * Helper to print flags fields */ static void print_bits32(char *label, struct bit_defs *bit, uint32_t state) { printf("%s0x%x (", label, state); while (bit->mask != 0) { if ((state & bit->mask) != 0) printf(" %s", bit->name); bit++; } printf(" )\n"); } /* * Command language: * */ const char helpstr[] = "Commands:\n" "help\t\tList commands\n" "show\t\tShow OpLock state etc.\n" "open FID\n" "close FID\n" "req FID [OplockLevel]\n" "ack FID [OplockLevel]\n" "brk-parent FID\n" "brk-open [OverWrite]\n" "brk-handle FID\n" "brk-read FID\n" "brk-write FID\n" "brk-setinfo FID [InfoClass]\n" "move FID1 FID2\n" "waiters FID [count]\n"; /* * Command handlers */ static void do_show(void) { smb_node_t *node = &test_node; smb_oplock_t *ol = &node->n_oplock; uint32_t state = ol->ol_state; smb_ofile_t *f; print_bits32(" ol_state=", state_bits, state); if (ol->excl_open != NULL) printf(" Excl=Y (FID=%d)", ol->excl_open->f_fid); else printf(" Excl=n"); printf(" cnt_II=%d cnt_R=%d cnt_RH=%d cnt_RHBQ=%d\n", ol->cnt_II, ol->cnt_R, ol->cnt_RH, ol->cnt_RHBQ); printf(" ofile_cnt=%d\n", node->n_ofile_list.ll_count); FOREACH_NODE_OFILE(node, f) { smb_oplock_grant_t *og = &f->f_oplock; printf(" fid=%d Lease=%s State=0x%x", f->f_fid, f->TargetOplockKey, /* lease */ og->og_state); if (og->og_breaking) printf(" BreakTo=0x%x", og->og_breakto); printf(" Excl=%s onlist:", (ol->excl_open == f) ? "Y" : "N"); if (og->onlist_II) printf(" II"); if (og->onlist_R) printf(" R"); if (og->onlist_RH) printf(" RH"); if (og->onlist_RHBQ) { printf(" RHBQ(to %s)", og->BreakingToRead ? "read" : "none"); } printf("\n"); } } static void do_open(int fid, char *arg2) { smb_node_t *node = &test_node; smb_ofile_t *ofile = &ofile_array[fid]; /* * Simulate an open (minimal init) */ if (ofile->f_refcnt) { printf("open fid %d already opened\n"); return; } if (arg2 != NULL) { (void) strlcpy((char *)ofile->TargetOplockKey, arg2, SMB_LEASE_KEY_SZ); } ofile->f_refcnt++; node->n_open_count++; smb_llist_insert_tail(&node->n_ofile_list, ofile); printf(" open %d OK\n", fid); } static void do_close(int fid) { smb_node_t *node = &test_node; smb_ofile_t *ofile = &ofile_array[fid]; /* * Simulate an close */ if (ofile->f_refcnt <= 0) { printf(" close fid %d already closed\n"); return; } smb_llist_enter(&node->n_ofile_list, RW_READER); mutex_enter(&node->n_oplock.ol_mutex); smb_oplock_break_CLOSE(ofile->f_node, ofile); smb_llist_remove(&node->n_ofile_list, ofile); node->n_open_count--; mutex_exit(&node->n_oplock.ol_mutex); smb_llist_exit(&node->n_ofile_list); ofile->f_refcnt--; bzero(ofile->TargetOplockKey, SMB_LEASE_KEY_SZ); printf(" close OK\n"); } static void do_req(int fid, char *arg2) { smb_ofile_t *ofile = &ofile_array[fid]; uint32_t oplock = BATCH_OPLOCK; uint32_t status; if (arg2 != NULL) oplock = strtol(arg2, NULL, 16); /* * Request an oplock */ status = smb_oplock_request(&test_sr, ofile, &oplock); if (status == 0 || status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { ofile->f_oplock.og_state = oplock; /* When no break pending, breakto=state */ ofile->f_oplock.og_breakto = oplock; ofile->f_oplock.og_breaking = B_FALSE; } printf(" req oplock fid=%d ret oplock=0x%x status=0x%x (%s)\n", fid, oplock, status, xlate_nt_status(status)); } static void do_ack(int fid, char *arg2) { smb_node_t *node = &test_node; smb_ofile_t *ofile = &ofile_array[fid]; uint32_t oplock; uint32_t status; /* Default to level in last smb_oplock_ind_break() */ oplock = last_ind_break_level; if (arg2 != NULL) oplock = strtol(arg2, NULL, 16); smb_llist_enter(&node->n_ofile_list, RW_READER); mutex_enter(&node->n_oplock.ol_mutex); ofile->f_oplock.og_breaking = 0; status = smb_oplock_ack_break(&test_sr, ofile, &oplock); if (status == 0) ofile->f_oplock.og_state = oplock; mutex_exit(&node->n_oplock.ol_mutex); smb_llist_exit(&node->n_ofile_list); if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { /* should not get this status */ printf(" ack: break fid=%d, break-in-progress\n", fid); ASSERT(0); } printf(" ack: break fid=%d, newstate=0x%x, status=0x%x (%s)\n", fid, oplock, status, xlate_nt_status(status)); run_ind_break_in_ack(ofile); } static void do_brk_parent(int fid) { smb_ofile_t *ofile = &ofile_array[fid]; uint32_t status; status = smb_oplock_break_PARENT(&test_node, ofile); printf(" brk-parent %d ret status=0x%x (%s)\n", fid, status, xlate_nt_status(status)); } static void do_brk_open(int fid, char *arg2) { smb_ofile_t *ofile = &ofile_array[fid]; uint32_t status; int disp = FILE_OPEN; if (arg2 != NULL) disp = strtol(arg2, NULL, 16); status = smb_oplock_break_OPEN(&test_node, ofile, 7, disp); printf(" brk-open %d ret status=0x%x (%s)\n", fid, status, xlate_nt_status(status)); } static void do_brk_handle(int fid) { smb_ofile_t *ofile = &ofile_array[fid]; uint32_t status; status = smb_oplock_break_HANDLE(&test_node, ofile); printf(" brk-handle %d ret status=0x%x (%s)\n", fid, status, xlate_nt_status(status)); } static void do_brk_read(int fid) { smb_ofile_t *ofile = &ofile_array[fid]; uint32_t status; status = smb_oplock_break_READ(ofile->f_node, ofile); printf(" brk-read %d ret status=0x%x (%s)\n", fid, status, xlate_nt_status(status)); } static void do_brk_write(int fid) { smb_ofile_t *ofile = &ofile_array[fid]; uint32_t status; status = smb_oplock_break_WRITE(ofile->f_node, ofile); printf(" brk-write %d ret status=0x%x (%s)\n", fid, status, xlate_nt_status(status)); } static void do_brk_setinfo(int fid, char *arg2) { smb_ofile_t *ofile = &ofile_array[fid]; uint32_t status; int infoclass = FileEndOfFileInformation; /* 20 */ if (arg2 != NULL) infoclass = strtol(arg2, NULL, 16); status = smb_oplock_break_SETINFO( &test_node, ofile, infoclass); printf(" brk-setinfo %d 0x%x ret status=0x%x (%s)\n", fid, infoclass, status, xlate_nt_status(status)); } /* * Move oplock to another FD, as specified, * or any other available open */ static void do_move(int fid, char *arg2) { smb_node_t *node = &test_node; smb_ofile_t *ofile = &ofile_array[fid]; smb_ofile_t *of2; int fid2; if (arg2 == NULL) { fprintf(stderr, "move: FID2 required\n"); return; } fid2 = atoi(arg2); if (fid2 <= 0 || fid2 >= MAXFID) { fprintf(stderr, "move: bad FID2 %d\n", fid2); return; } of2 = &ofile_array[fid2]; mutex_enter(&node->n_oplock.ol_mutex); smb_oplock_move(&test_node, ofile, of2); mutex_exit(&node->n_oplock.ol_mutex); printf(" move %d %d\n", fid, fid2); } /* * Set/clear oplock.waiters, which affects ack-break */ static void do_waiters(int fid, char *arg2) { smb_node_t *node = &test_node; smb_oplock_t *ol = &node->n_oplock; int old, new = 0; if (arg2 != NULL) new = atoi(arg2); old = ol->waiters; ol->waiters = new; printf(" waiters %d -> %d\n", old, new); } int main(int argc, char *argv[]) { smb_node_t *node = &test_node; char *cmd; char *arg1; char *arg2; char *savep; char *sep = " \t\n"; char *prompt = NULL; int fid; if (isatty(0)) prompt = "> "; mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL); smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), offsetof(smb_ofile_t, f_node_lnd)); for (fid = 0; fid < MAXFID; fid++) { smb_ofile_t *f = &ofile_array[fid]; f->f_magic = SMB_OFILE_MAGIC; mutex_init(&f->f_mutex, NULL, MUTEX_DEFAULT, NULL); f->f_fid = fid; f->f_ftype = SMB_FTYPE_DISK; f->f_node = &test_node; } for (;;) { if (prompt) { (void) fputs(prompt, stdout); (void) fflush(stdout); } cmd = fgets(cmdbuf, sizeof (cmdbuf), stdin); if (cmd == NULL) break; if (cmd[0] == '#') continue; if (prompt == NULL) { /* Put commands in the output too. */ (void) fputs(cmdbuf, stdout); } cmd = strtok_r(cmd, sep, &savep); if (cmd == NULL) continue; /* * Commands with no args */ if (0 == strcmp(cmd, "help")) { (void) fputs(helpstr, stdout); continue; } if (0 == strcmp(cmd, "show")) { do_show(); continue; } /* * Commands with one arg (the FID) */ arg1 = strtok_r(NULL, sep, &savep); if (arg1 == NULL) { fprintf(stderr, "%s missing arg1\n", cmd); continue; } fid = atoi(arg1); if (fid <= 0 || fid >= MAXFID) { fprintf(stderr, "%s bad FID %d\n", cmd, fid); continue; } if (0 == strcmp(cmd, "close")) { do_close(fid); continue; } if (0 == strcmp(cmd, "brk-parent")) { do_brk_parent(fid); continue; } if (0 == strcmp(cmd, "brk-handle")) { do_brk_handle(fid); continue; } if (0 == strcmp(cmd, "brk-read")) { do_brk_read(fid); continue; } if (0 == strcmp(cmd, "brk-write")) { do_brk_write(fid); continue; } /* * Commands with an (optional) arg2. */ arg2 = strtok_r(NULL, sep, &savep); if (0 == strcmp(cmd, "open")) { do_open(fid, arg2); continue; } if (0 == strcmp(cmd, "req")) { do_req(fid, arg2); continue; } if (0 == strcmp(cmd, "ack")) { do_ack(fid, arg2); continue; } if (0 == strcmp(cmd, "brk-open")) { do_brk_open(fid, arg2); continue; } if (0 == strcmp(cmd, "brk-setinfo")) { do_brk_setinfo(fid, arg2); continue; } if (0 == strcmp(cmd, "move")) { do_move(fid, arg2); continue; } if (0 == strcmp(cmd, "waiters")) { do_waiters(fid, arg2); continue; } fprintf(stderr, "%s unknown command. Try help\n", cmd); } return (0); } /* * A few functions called by the oplock code * Stubbed out, and/or just print a message. */ boolean_t smb_node_is_file(smb_node_t *node) { return (B_TRUE); } boolean_t smb_ofile_is_open(smb_ofile_t *ofile) { return (ofile->f_refcnt != 0); } int smb_lock_range_access( smb_request_t *sr, smb_node_t *node, uint64_t start, uint64_t length, boolean_t will_write) { return (0); } /* * Test code replacement for combination of: * smb_oplock_hdl_update() * smb_oplock_send_break() * * In a real server, we would send a break to the client, * and keep track (at the SMB level) whether this oplock * was obtained via a lease or an old-style oplock. */ static void test_oplock_send_break(smb_ofile_t *ofile, uint32_t NewLevel, boolean_t AckReq) { smb_oplock_grant_t *og = &ofile->f_oplock; uint32_t OldLevel; /* Skip building a message. */ if ((og->og_state & OPLOCK_LEVEL_GRANULAR) != 0) NewLevel |= OPLOCK_LEVEL_GRANULAR; OldLevel = og->og_state; og->og_breakto = NewLevel; og->og_breaking = B_TRUE; printf("*smb_oplock_send_break fid=%d " "NewLevel=0x%x, OldLevel=0x%x, AckReq=%d)\n", ofile->f_fid, NewLevel, OldLevel, AckReq); if (!AckReq) { og->og_state = NewLevel; og->og_breaking = B_FALSE; } /* Next, smb_oplock_send_break() would send a break. */ last_ind_break_level = NewLevel; } /* * Simplified version of what's in smb_srv_oplock.c */ void smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel, boolean_t AckReq, uint32_t status) { smb_oplock_grant_t *og = &ofile->f_oplock; printf("*smb_oplock_ind_break fid=%d NewLevel=0x%x," " AckReq=%d, ComplStatus=0x%x (%s)\n", ofile->f_fid, NewLevel, AckReq, status, xlate_nt_status(status)); /* * Note that the CompletionStatus from the FS level * (smb_cmn_oplock.c) encodes what kind of action we * need to take at the SMB level. */ switch (status) { case NT_STATUS_SUCCESS: case NT_STATUS_CANNOT_GRANT_REQUESTED_OPLOCK: test_oplock_send_break(ofile, NewLevel, AckReq); break; case NT_STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE: case NT_STATUS_OPLOCK_HANDLE_CLOSED: og->og_state = OPLOCK_LEVEL_NONE; og->og_breakto = OPLOCK_LEVEL_NONE; og->og_breaking = B_FALSE; break; default: ASSERT(0); break; } } /* Arrange for break_in_ack to run after ack completes. */ static uint32_t break_in_ack_NewLevel; static boolean_t break_in_ack_AckReq; static boolean_t break_in_ack_called; void smb_oplock_ind_break_in_ack(smb_request_t *sr, smb_ofile_t *ofile, uint32_t NewLevel, boolean_t AckRequired) { ASSERT(sr == &test_sr); /* Process these after ack */ ASSERT(!break_in_ack_called); break_in_ack_called = B_TRUE; break_in_ack_NewLevel = NewLevel; break_in_ack_AckReq = AckRequired; } static void run_ind_break_in_ack(smb_ofile_t *ofile) { uint32_t NewLevel; boolean_t AckReq; /* Process these after ack */ if (!break_in_ack_called) return; break_in_ack_called = B_FALSE; NewLevel = break_in_ack_NewLevel; AckReq = break_in_ack_AckReq; printf("*smb_oplock_ind_break_in_ack fid=%d NewLevel=0x%x," " AckReq=%d\n", ofile->f_fid, NewLevel, AckReq); test_oplock_send_break(ofile, NewLevel, AckReq); } uint32_t smb_oplock_wait_break(smb_request_t *sr, smb_node_t *node, int timeout) { printf("*smb_oplock_wait_break (state=0x%x)\n", node->n_oplock.ol_state); return (0); } int smb_fem_oplock_install(smb_node_t *node) { return (0); } void smb_fem_oplock_uninstall(smb_node_t *node) { } /* * There are a couple DTRACE_PROBE* in smb_cmn_oplock.c but we're * not linking with the user-level dtrace support, so just * stub these out. */ void __dtrace_fksmb___probe1(char *n, unsigned long a) { } void __dtrace_fksmb___probe2(char *n, unsigned long a, unsigned long b) { } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * A few excerpts from smb_kutil.c */ #include #include #include #include #include #include #include /* * smb_llist_constructor * * This function initializes a locked list. */ void smb_llist_constructor( smb_llist_t *ll, size_t size, size_t offset) { rw_init(&ll->ll_lock, NULL, RW_DEFAULT, NULL); mutex_init(&ll->ll_mutex, NULL, MUTEX_DEFAULT, NULL); list_create(&ll->ll_list, size, offset); list_create(&ll->ll_deleteq, sizeof (smb_dtor_t), offsetof(smb_dtor_t, dt_lnd)); ll->ll_count = 0; ll->ll_wrop = 0; ll->ll_deleteq_count = 0; ll->ll_flushing = B_FALSE; } /* * Flush the delete queue and destroy a locked list. */ void smb_llist_destructor( smb_llist_t *ll) { /* smb_llist_flush(ll); */ ASSERT(ll->ll_count == 0); ASSERT(ll->ll_deleteq_count == 0); rw_destroy(&ll->ll_lock); list_destroy(&ll->ll_list); list_destroy(&ll->ll_deleteq); mutex_destroy(&ll->ll_mutex); } void smb_llist_enter(smb_llist_t *ll, krw_t mode) { rw_enter(&ll->ll_lock, mode); } /* * Exit the list lock and process the delete queue. */ void smb_llist_exit(smb_llist_t *ll) { rw_exit(&ll->ll_lock); /* smb_llist_flush(ll); */ } /* * smb_llist_upgrade * * This function tries to upgrade the lock of the locked list. It assumes the * locked has already been entered in RW_READER mode. It first tries using the * Solaris function rw_tryupgrade(). If that call fails the lock is released * and reentered in RW_WRITER mode. In that last case a window is opened during * which the contents of the list may have changed. The return code indicates * whether or not the list was modified when the lock was exited. */ int smb_llist_upgrade( smb_llist_t *ll) { uint64_t wrop; if (rw_tryupgrade(&ll->ll_lock) != 0) { return (0); } wrop = ll->ll_wrop; rw_exit(&ll->ll_lock); rw_enter(&ll->ll_lock, RW_WRITER); return (wrop != ll->ll_wrop); } /* * smb_llist_insert_head * * This function inserts the object passed a the beginning of the list. This * function assumes the lock of the list has already been entered. */ void smb_llist_insert_head( smb_llist_t *ll, void *obj) { list_insert_head(&ll->ll_list, obj); ++ll->ll_wrop; ++ll->ll_count; } /* * smb_llist_insert_tail * * This function appends to the object passed to the list. This function assumes * the lock of the list has already been entered. * */ void smb_llist_insert_tail( smb_llist_t *ll, void *obj) { list_insert_tail(&ll->ll_list, obj); ++ll->ll_wrop; ++ll->ll_count; } /* * smb_llist_remove * * This function removes the object passed from the list. This function assumes * the lock of the list has already been entered. */ void smb_llist_remove( smb_llist_t *ll, void *obj) { list_remove(&ll->ll_list, obj); ++ll->ll_wrop; --ll->ll_count; } /* * smb_llist_get_count * * This function returns the number of elements in the specified list. */ uint32_t smb_llist_get_count( smb_llist_t *ll) { return (ll->ll_count); }