pax_global_header00006660000000000000000000000064141722652600014517gustar00rootroot0000000000000052 comment=686f15e9db15014098faa3fedce537558e8fb056 ksmbd-tools-3.4.4/000077500000000000000000000000001417226526000137655ustar00rootroot00000000000000ksmbd-tools-3.4.4/.travis.yml000066400000000000000000000004361417226526000161010ustar00rootroot00000000000000dist: bionic language: c notifications: - email: true before_install: - sudo apt-get install libnl-3-dev libnl-genl-3-dev libkrb5-dev - gcc --version - g++ --version script: # Compilation - ./autogen.sh - ./configure - make DISTCHECK_CONFIGURE_FLAGS=--enable-krb5 distcheck ksmbd-tools-3.4.4/AUTHORS000066400000000000000000000016271417226526000150430ustar00rootroot00000000000000Original Author and Creator =========================== Namjae Jeon Maintainers =============== Namjae Jeon Sergey Senozhatsky Patch Contributors and Developers --------------------------------- Sergey Senozhatsky (Resolve overall architecture issues and make close to the mainline. and his work cause smbd 2.0 to be reborn) Taeyang Mok (various smbd works) Yunjae Lim (compatibility works for several windows version and MS-SMB2 testsuite) Hyunchul Lee (SMBDirect works) Gibeom Kim Pankaj Sharma Anupam Aggarwal Mayank Singh kumar sourav Vivek Trivedi Amit Sahrawat ksmbd-tools-3.4.4/COPYING000066400000000000000000000432341417226526000150260ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ksmbd-tools-3.4.4/Documentation/000077500000000000000000000000001417226526000165765ustar00rootroot00000000000000ksmbd-tools-3.4.4/Documentation/configuration.txt000066400000000000000000000226701417226526000222150ustar00rootroot00000000000000 Define ksmbd configuration parameters list. * Supported [global] level parameters list: - server string (default: SMB SERVER) This controls what string will show up in browse lists next to the machine name - workgroup (default : WORKGROUP) This controls what workgroup your server will appear to be in when queried by clients - netbios name (default : SMBD SERVER) This sets the NetBIOS name by which a SMB server is known. By default it is the same as the first component of the host's DNS name. If a machine is a browse server or logon server this name (or the first component of the hosts DNS name) will be the name that these services are advertised under. - server min protocol (default: SMB2_10) This setting controls the minimum protocol version that the server will allow the client to use. - server max protocol (default: SMB3_11) The value of the parameter (a string) is the highest protocol level that will be supported by the server. - server signing (default: disabled) This controls whether the client is allowed or required to use SMB1 and SMB2 signing. Possible values are auto, mandatory and disabled. - guest account (default: nobody) This is a username which will be used for access to services which are specified as guest ok. - max active sessions (default: 1024) This option allows the number of simultaneous connections to a service to be limited. - ipc timeout (default: 0) This option specifies the number of seconds server will wait for the userspace to reply to heartbeat frames. If user space is down for more than `ipc timeout` seconds the server will reset itself - close all sessions and all TCP connections. - restrict anonymous (default: 0) The setting of this parameter determines whether user and group list information is returned for an anonymous connection. - map to guest (default: never) This parameter can take four different values, which tell ksmbd what to do with user login requests.(never, bad user, bad password, bad uid) - bind interfaces only (default: no) This global parameter allows the ksmbd admin to limit what interfaces on a machine will serve SMB requests. - interfaces (default: none) This option allows you to override the default network interfaces list that ksmbd will use for browsing. The option takes only list of interface name. - deadtime (default: 0) The value of the parameter (a decimal integer) represents the number of minutes of inactivity before a connection is considered dead, and it is disconnected. The deadtime only takes effect if the number of open files is zero. - root directory (default: none) Sets up a root (base) directory for all shares. In some sense it's equal to chroot(). When this option set all shares' paths become relative to root directory, IOW the file name lookup resolves '/root directory/share path/file path' path. - max open files (default: 10000) This parameter limits the maximum number of open files that one ksmbd file serving process may have open for a client at any one time. - tcp port (default: 445) Specifies which ports the server should listen on for SMB traffic. - smb2 max read (default: 4MB) This option specifies the protocol value that ksmbd will return to a client, informing the client of the largest size that may be returned by a single SMB2 read call. - smb2 max write (default: 4MB) This option specifies the protocol value that ksmbd will return to a client, informing the client of the largest size that may be sent to the server by a single SMB2 write call. - smb2 max trans (default: 1MB) This option specifies the protocol value that ksmbd will return to a client, informing the client of the largest size of buffer that may be used in querying file meta-data via QUERY_INFO and related SMB2 calls. - smb3 encryption (default: no) This parameter controls whether a remote client is allowed or required to use SMB encryption. - smb2 leases (default: no) This boolean option tells ksmbd whether to globally negotiate SMB2 leases on file open requests. Leasing is an SMB2-only feature which allows clients to aggressively cache files locally. - share:fake_fscaps (default: 64) This is needed to support some special application that makes QFSINFO calls to check whether we set the SPARSE_FILES bit (0x40). If this bit is not set that particular application refuses to work against Samba. With share:fake_fscaps = 64 the SPARSE_FILES file system capability flag is set. Use other decimal values to specify the bitmask you need to fake. - kerberos keytab file This option tells ksmbd where the path of the keytab file which stores the ksmbd service's password can be found. If this option is not given, ksmbd try to find the file in system default location. - kerberos service name This option tells ksmbd the fully qualified domain name of the host where ksmbd runs. the format is "cifs/". if this option is not given, ksmbd sets "cifs" to the service name and try to get the host FQDN using getaddrinfo(3). - server multi channel support (default: no) This boolean parameter controls whether ksmbd will support SMB3 multi-channel. Warn that this is experimental feature which means data can be corrupted under race conditions. - smb2 max credits (default: 8192) This option controls the maximum number of outstanding simultaneous SMB2 operations. * Supported [share] level parameters list: - comment (default: none) comment string to associate with the new share - path (default: none) This parameter specifies a directory to which the user of the service is to be given access. - guest ok (default: no) If this parameter is yes for a service, then no password is required to connect to the service. - read only (default: yes) If this parameter is yes, then users of a service may not create or modify files in the service's directory. - browseable (default: yes) This controls whether this share is seen in the list of available shares in a net view and in the browse list. - write ok (default: no) - writeable (default: no) Inverted synonym for read only. - store dos attributes (default: yes) If this parameter is set ksmbd attempts to first read DOS attributes (SYSTEM, HIDDEN, ARCHIVE or READ-ONLY) from a filesystem extended attribute, before mapping DOS attributes to UNIX permission bits (such as occurs with map hidden and map readonly). - oplocks (default: yes) This boolean option tells ksmbd whether to issue oplocks (opportunistic locks) to file open requests on this share. - create mask (default: 0744) When a file is created, the necessary permissions are calculated according to the mapping from DOS modes to UNIX permissions, and the resulting UNIX mode is then bit-wise 'AND'ed with this parameter. - directory mask (default: 0755) This parameter is the octal modes which are used when converting DOS modes to UNIX modes when creating UNIX directories. - force group (default: 0) This specifies a UNIX group name that will be assigned as the default primary group for all users connecting to this service. - force user (default: 0) This specifies a UNIX user name that will be assigned as the default user for all users connecting to this service. - hide dot files (default: yes) This is a boolean parameter that controls whether files starting with a dot appear as hidden files. - hosts allow (default: none) This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service - hosts deny (default: none) The opposite of allow hosts - hosts listed here are NOT permitted access to services unless the specific services have their own lists to override this one. Where the lists conflict, the allow list takes precedence. - valid users (default: none) This is a list of users that should be allowed to login to this service. - invalid users (default: none) This is a list of users that should not be allowed to login to this service. - read list (default: none) This is a list of users that are given read-only access to a service. - write list (default: none) This is a list of users that are given read-write access to a service. - max connections (default: 0) This option allows the number of simultaneous connections to a service to be limited. - veto files (default: none) This is a list of files and directories that are neither visible nor accessible. Veto any files containing the word Security, any ending in .tmp, and any directory containing the word root. veto files = /*Security*/*.tmp/*root*/ Veto the Apple specific files that a NetAtalk server creates. veto files = /.AppleDouble/.bin/.AppleDesktop/Network Trash Folder/ - inherit owner (default: no) The ownership for new files and directories should be controlled by the ownership of the parent directory. Valid options are yes or no. - force create mode (default: 0) This parameter specifies a set of UNIX mode bit permissions that will always be set on a file created by ksmbd. - force directory mode (default: 0) This parameter specifies a set of UNIX mode bit permissions that will always be set on a directory created by ksmbd. - vfs objects (default: no) This parameter specifies the backend names which are used for ksmbd VFS I/O operations. Since ksmbd is a server for Linux Kernel, It supports acl_xattr and streams_xattr input. (ex. vfs objects = acl_xattr, vfs objects = streams_xattr or vfs objects = acl_xattr streams_xattr) ksmbd-tools-3.4.4/Makefile.am000066400000000000000000000002001417226526000160110ustar00rootroot00000000000000## Makefile.am ACLOCAL_AMFLAGS = -I m4 SUBDIRS = lib mountd adduser addshare control # other stuff EXTRA_DIST = \ include ksmbd-tools-3.4.4/PFIF.txt000066400000000000000000000002721417226526000152530ustar00rootroot00000000000000This code was developed in participation with the Protocol Freedom Information Foundation. Please see http://protocolfreedom.org/ and http://samba.org/samba/PFIF/ for more details. ksmbd-tools-3.4.4/README000066400000000000000000000043741417226526000146550ustar00rootroot00000000000000________________________ BUILDING KSMBD TOOLS ________________________ Install preprequisite packages: For Ubuntu: sudo apt-get install autoconf libtool pkg-config libnl-3-dev \ libnl-genl-3-dev libglib2.0-dev For Fedora, RHEL: sudo yum install autoconf automake libtool glib2-devel libnl3-devel For CentOS: sudo yum install glib2-devel libnl3-devel ksmbd-tools.spec should serve as a base template for RPM packagers. Build steps: - cd into the ksmbd-tools directory - ./autogen.sh - ./configure - make - make install _____________________ USING KSMBD TOOLS _____________________ Setup steps: - install smbd kernel driver modprobe ksmbd - create user/password for SMB share mkdir /etc/ksmbd/ ksmbd.adduser -a Enter password for SMB share access - create /etc/ksmbd/smb.conf file, add SMB share in smb.conf file Refer smb.conf.example - start smbd user space daemon ksmbd.mountd - access share from Windows or Linux using CIFS _____________________ RESTART KSMBD _____________________ steps: - kill user and kernel space daemon sudo ksmbd.control -s - restart user space daemon ksmbd.mountd _____________________ Shutdown KSMBD _____________________ steps: - kill user and kernel space daemon sudo ksmbd.control -s - unload ksmbd module rmmod ksmbd _____________________ Enable debug prints _____________________ steps: - Enable all component prints sudo ksmbd.control -d "all" - Enable one of components(smb, auth, vfs, oplock, ipc, conn, rdma) sudo ksmbd.control -d "smb" - Disable prints: If you try the selected component once more, It is disabled without brackets. -------------------- ADMIN TOOLS -------------------- - ksmbd.adduser Adds, updates or removes (-a/-u/-d) a user from ksmbd pwd file. - ksmbd.addshare Adds, updates or removes (-a/-u/-d) a net share from smb.conf file. Usage example: Creating a new share: ksmbd.addshare -a files -o "\ path=/home/users/files \ comment=exported files \ writeable=yes \ read only = no \ " Note that share options (-o) must always be enquoted ("..."). ksmbd.addshare tool does not modify [global] smb.conf section; only net share configs are supported at the moment. ksmbd-tools-3.4.4/README.md000066400000000000000000000041061417226526000152450ustar00rootroot00000000000000# ksmbd-tools ### Building ##### Install prerequisite packages: - For Ubuntu: - `sudo apt-get install autoconf libtool pkg-config libnl-3-dev libnl-genl-3-dev libglib2.0-dev` - For Fedora, RHEL: - `sudo yum install autoconf automake libtool glib2-devel libnl3-devel` - For CentOS: - `sudo yum install glib2-devel libnl3-devel` - For openSUSE: - `sudo zypper install glib2-devel libnl3-devel` ##### Building: - clone this repository - `cd ksmbd-tools` - `./autogen.sh` - `./configure` - `make` - `make install` ### Usage All administration tasks must be done as root. ##### Setup: - Install ksmbd kernel driver - `modprobe ksmbd` - Create user/password for SMB share - `mkdir /etc/ksmbd` - `ksmbd.adduser -a ` - Enter password for SMB share access - Create `/etc/ksmbd/smb.conf` file - Refer `smb.conf.example` - Add share to `smb.conf` - This can be done manually or with `ksmbd.addshare`, e.g.: - `ksmbd.addshare -a myshare -o "guest ok = yes, writable = yes, path = /mnt/data"` - Note: share options (-o) must always be enclosed with double quotes ("..."). - Start ksmbd user space daemon - `ksmbd.mountd` - Access share from Windows or Linux using CIFS ##### Stopping and restarting the daemon: First, kill user and kernel space daemon - `ksmbd.control -s` Then, to restart the daemon, run: - `ksmbd.mountd` Or to shut it down completely: - `rmmod ksmbd` ### Debugging - Enable all component prints - `ksmbd.control -d "all"` - Enable a single component (see below) - `ksmbd.control -d "smb"` - Run the command with the same component name again to disable it Currently available debug components: smb, auth, vfs, oplock, ipc, conn, rdma ### More... - ksmbd.adduser - Adds (-a), updates (-u), or deletes (-d) a user from user database. - Default database file is `/etc/ksmbd/users.db` - ksmbd.addshare - Adds (-a), updates (-u), or deletes (-d) a net share from config file. - Default config file is `/etc/ksmbd/smb.conf` `ksmbd.addshare` does not modify `[global]` section in config file; only net share configs are supported at the moment. ksmbd-tools-3.4.4/addshare/000077500000000000000000000000001417226526000155405ustar00rootroot00000000000000ksmbd-tools-3.4.4/addshare/Makefile.am000066400000000000000000000003771417226526000176030ustar00rootroot00000000000000AM_CFLAGS = -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common LIBS = $(GLIB_LIBS) ksmbd_addshare_LDADD = $(top_builddir)/lib/libksmbdtools.a sbin_PROGRAMS = ksmbd.addshare ksmbd_addshare_SOURCES = share_admin.c addshare.c share_admin.h ksmbd-tools-3.4.4/addshare/addshare.c000066400000000000000000000065001417226526000174600ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2019 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #include #include #include "config_parser.h" #include "ksmbdtools.h" #include "management/share.h" #include "linux/ksmbd_server.h" #include "share_admin.h" #include "version.h" static char *arg_name; static char *arg_opts; enum { COMMAND_ADD_SHARE = 1, COMMAND_DEL_SHARE, COMMAND_UPDATE_SHARE, }; static void usage(void) { int i; fprintf(stderr, "Usage: smbshareadd\n"); fprintf(stderr, "\t-a | --add-share=share\n"); fprintf(stderr, "\t-d | --del-share=share\n"); fprintf(stderr, "\t-u | --update-share=share\n"); fprintf(stderr, "\t-o | --options=\"op1=val1 op2=val2...\"\n"); fprintf(stderr, "\t-c smb.conf\n"); fprintf(stderr, "\t-V | --version\n"); fprintf(stderr, "\t-v | --verbose\n"); fprintf(stderr, "Supported share options:\n"); for (i = 0; i < KSMBD_SHARE_CONF_MAX; i++) fprintf(stderr, "\t%s\n", KSMBD_SHARE_CONF[i]); exit(EXIT_FAILURE); } static void show_version(void) { printf("ksmbd-tools version : %s\n", KSMBD_TOOLS_VERSION); exit(EXIT_FAILURE); } static int parse_configs(char *smbconf) { int ret; ret = test_file_access(smbconf); if (ret) return ret; ret = cp_smbconfig_hash_create(smbconf); if (ret) return ret; return 0; } static int sanity_check_share_name_simple(char *name) { int sz, i; if (!name) return -EINVAL; sz = strlen(name); if (sz < 1) return -EINVAL; if (sz >= KSMBD_REQ_MAX_SHARE_NAME) return -EINVAL; if (!cp_key_cmp(name, "global")) return -EINVAL; return -EINVAL; } int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; char *smbconf = PATH_SMBCONF; int c, cmd = 0; set_logger_app_name("ksmbd.addshare"); opterr = 0; while ((c = getopt(argc, argv, "c:a:d:u:p:o:Vvh")) != EOF) switch (c) { case 'a': arg_name = g_ascii_strdown(optarg, strlen(optarg)); cmd = COMMAND_ADD_SHARE; break; case 'd': arg_name = g_ascii_strdown(optarg, strlen(optarg)); cmd = COMMAND_DEL_SHARE; break; case 'u': arg_name = g_ascii_strdown(optarg, strlen(optarg)); cmd = COMMAND_UPDATE_SHARE; break; case 'c': smbconf = strdup(optarg); break; case 'o': arg_opts = strdup(optarg); break; case 'V': show_version(); break; case 'v': break; case '?': case 'h': default: usage(); } if (cmd != COMMAND_DEL_SHARE && !arg_opts) { usage(); return -1; } if (sanity_check_share_name_simple(arg_name)) { pr_err("share name sanity check failure\n"); goto out; } if (!smbconf) { pr_err("Out of memory\n"); goto out; } ret = parse_configs(smbconf); if (ret) { pr_err("Unable to parse configuration files\n"); goto out; } if (cmd == COMMAND_ADD_SHARE) ret = command_add_share(smbconf, arg_name, arg_opts); if (cmd == COMMAND_DEL_SHARE) ret = command_del_share(smbconf, arg_name); if (cmd == COMMAND_UPDATE_SHARE) ret = command_update_share(smbconf, arg_name, arg_opts); /* * We support only ADD_SHARE command for the time being */ if (ret == 0 && cmd == COMMAND_ADD_SHARE) notify_ksmbd_daemon(); out: cp_smbconfig_destroy(); return ret; } ksmbd-tools-3.4.4/addshare/share_admin.c000066400000000000000000000105161417226526000201610ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2019 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #include #include #include static int conf_fd = -1; static char wbuf[16384]; static size_t wsz; #define AUX_GROUP_PREFIX "_a_u_x_grp_" static char *new_group_name(char *name) { char *gn; if (strchr(name, '[')) return name; gn = g_malloc(strlen(name) + 3); if (gn) sprintf(gn, "[%s]", name); return gn; } static char *aux_group_name(char *name) { char *gn; gn = g_malloc(strlen(name) + 3 + strlen(AUX_GROUP_PREFIX)); if (gn) sprintf(gn, "[%s%s]", AUX_GROUP_PREFIX, name); return gn; } static int __open_smbconf(char *smbconf) { conf_fd = open(smbconf, O_WRONLY); if (conf_fd == -1) { pr_err("%s %s\n", strerr(errno), smbconf); return -EINVAL; } if (ftruncate(conf_fd, 0)) { pr_err("%s %s\n", strerr(errno), smbconf); close(conf_fd); return -EINVAL; } return 0; } static void __write(void) { int nr = 0; int ret; while (wsz && (ret = write(conf_fd, wbuf + nr, wsz)) != 0) { if (ret == -1) { if (errno == EINTR) continue; pr_err("%s\n", strerr(errno)); exit(EXIT_FAILURE); } nr += ret; wsz -= ret; } } static void __write_share(gpointer key, gpointer value, gpointer buf) { char *k = (char *)key; char *v = (char *)value; wsz = snprintf(wbuf, sizeof(wbuf), "\t%s = %s\n", k, v); if (wsz > sizeof(wbuf)) { pr_err("smb.conf entry size is above the limit: %zu > %zu\n", wsz, sizeof(wbuf)); exit(EXIT_FAILURE); } __write(); } static void write_share(struct smbconf_group *g) { wsz = snprintf(wbuf, sizeof(wbuf), "[%s]\n", g->name); __write(); g_hash_table_foreach(g->kv, __write_share, NULL); } static void write_share_cb(gpointer key, gpointer value, gpointer share_data) { struct smbconf_group *g = (struct smbconf_group *)value; /* * Do not write AUX group */ if (!strstr(g->name, AUX_GROUP_PREFIX)) write_share(g); } static void write_remove_share_cb(gpointer key, gpointer value, gpointer name) { struct smbconf_group *g = (struct smbconf_group *)value; if (!g_ascii_strcasecmp(g->name, name)) { pr_info("share '%s' removed\n", g->name); return; } write_share(g); } static void update_share_cb(gpointer key, gpointer value, gpointer g) { char *nk, *nv; nk = g_strdup(key); nv = g_strdup(value); if (!nk || !nv) exit(EXIT_FAILURE); /* This will call .dtor for already existing key/value pairs */ g_hash_table_insert(g, nk, nv); } int command_add_share(char *smbconf, char *name, char *opts) { char *new_name = NULL; if (g_hash_table_lookup(parser.groups, name)) { pr_err("Share already exists: %s\n", name); return -EEXIST; } new_name = new_group_name(name); if (cp_parse_external_smbconf_group(new_name, opts)) goto error; if (__open_smbconf(smbconf)) goto error; g_hash_table_foreach(parser.groups, write_share_cb, NULL); close(conf_fd); g_free(new_name); return 0; error: g_free(new_name); return -EINVAL; } int command_update_share(char *smbconf, char *name, char *opts) { struct smbconf_group *existing_group; struct smbconf_group *update_group; char *aux_name = NULL; existing_group = g_hash_table_lookup(parser.groups, name); if (!existing_group) { pr_err("Unknown share: %s\n", name); goto error; } aux_name = aux_group_name(name); if (cp_parse_external_smbconf_group(aux_name, opts)) goto error; /* get rid of [] */ sprintf(aux_name, "%s%s", AUX_GROUP_PREFIX, name); update_group = g_hash_table_lookup(parser.groups, aux_name); if (!update_group) { pr_err("Cannot find the external group\n"); goto error; } g_hash_table_foreach(update_group->kv, update_share_cb, existing_group->kv); if (__open_smbconf(smbconf)) goto error; g_hash_table_foreach(parser.groups, write_share_cb, NULL); close(conf_fd); g_free(aux_name); return 0; error: g_free(aux_name); return -EINVAL; } int command_del_share(char *smbconf, char *name) { if (__open_smbconf(smbconf)) return -EINVAL; g_hash_table_foreach(parser.groups, write_remove_share_cb, name); close(conf_fd); return 0; } ksmbd-tools-3.4.4/addshare/share_admin.h000066400000000000000000000006661417226526000201730ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2019 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __KSMBD_SHARE_ADMIN_H__ #define __KSMBD_SHARE_ADMIN_H__ int command_add_share(char *smbconf, char *name, char *opts); int command_update_share(char *smbconf, char *name, char *opts); int command_del_share(char *smbconf, char *name); #endif /* __KSMBD_SHARE_ADMIN_H__ */ ksmbd-tools-3.4.4/adduser/000077500000000000000000000000001417226526000154145ustar00rootroot00000000000000ksmbd-tools-3.4.4/adduser/Makefile.am000066400000000000000000000004171417226526000174520ustar00rootroot00000000000000AM_CFLAGS = -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common LIBS = $(GLIB_LIBS) ksmbd_adduser_LDADD = $(top_builddir)/lib/libksmbdtools.a sbin_PROGRAMS = ksmbd.adduser ksmbd_adduser_SOURCES = md4_hash.c user_admin.c adduser.c md4_hash.h user_admin.h ksmbd-tools-3.4.4/adduser/adduser.c000066400000000000000000000065201417226526000172120ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #include #include #include "config_parser.h" #include "ksmbdtools.h" #include "management/user.h" #include "management/share.h" #include "user_admin.h" #include "linux/ksmbd_server.h" #include "version.h" static char *arg_account = NULL; static char *arg_password = NULL; enum { COMMAND_ADD_USER = 1, COMMAND_DEL_USER, COMMAND_UPDATE_USER, }; static void usage(void) { fprintf(stderr, "Usage: smbuseradd\n"); fprintf(stderr, "\t-a | --add-user=login\n"); fprintf(stderr, "\t-d | --del-user=login\n"); fprintf(stderr, "\t-u | --update-user=login\n"); fprintf(stderr, "\t-p | --password=pass\n"); fprintf(stderr, "\t-i smbpwd.db | --import-users=smbpwd.db\n"); fprintf(stderr, "\t-V | --version\n"); fprintf(stderr, "\t-v | --verbose\n"); exit(EXIT_FAILURE); } static void show_version(void) { printf("ksmbd-tools version : %s\n", KSMBD_TOOLS_VERSION); exit(EXIT_FAILURE); } static int parse_configs(char *pwddb) { int ret; ret = test_file_access(pwddb); if (ret) return ret; ret = cp_parse_pwddb(pwddb); if (ret) return ret; return 0; } static int sanity_check_user_name_simple(char *uname) { int sz, i; if (!uname) return -EINVAL; sz = strlen(uname); if (sz < 1) return -EINVAL; if (sz >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) return -EINVAL; /* 1'; Drop table users -- */ if (!strcmp(uname, "root")) return -EINVAL; if (strpbrk(uname, ":\n")) return -EINVAL; return 0; } int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; char *pwddb = PATH_PWDDB; int c, cmd = 0; set_logger_app_name("ksmbd.adduser"); opterr = 0; while ((c = getopt(argc, argv, "c:i:a:d:u:p:Vvh")) != EOF) switch (c) { case 'a': arg_account = g_strdup(optarg); cmd = COMMAND_ADD_USER; break; case 'd': arg_account = g_strdup(optarg); cmd = COMMAND_DEL_USER; break; case 'u': arg_account = g_strdup(optarg); cmd = COMMAND_UPDATE_USER; break; case 'p': arg_password = g_strdup(optarg); break; case 'i': pwddb = g_strdup(optarg); break; case 'V': show_version(); break; case 'v': break; case '?': case 'h': default: usage(); } if (sanity_check_user_name_simple(arg_account)) { pr_err("User name sanity check failure\n"); goto out; } if (!pwddb) { pr_err("Out of memory\n"); goto out; } ret = usm_init(); if (ret) { pr_err("Failed to init user management\n"); goto out; } ret = shm_init(); if (ret) { pr_err("Failed to init net share management\n"); goto out; } ret = parse_configs(pwddb); if (ret) { pr_err("Unable to parse configuration files\n"); goto out; } if (cmd == COMMAND_ADD_USER) ret = command_add_user(pwddb, arg_account, arg_password); if (cmd == COMMAND_DEL_USER) ret = command_del_user(pwddb, arg_account); if (cmd == COMMAND_UPDATE_USER) ret = command_update_user(pwddb, arg_account, arg_password); /* * We support only ADD_USER command at this moment */ if (ret == 0 && cmd == COMMAND_ADD_USER) notify_ksmbd_daemon(); out: shm_destroy(); usm_destroy(); return ret; } ksmbd-tools-3.4.4/adduser/md4_hash.c000066400000000000000000000123531417226526000172530ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Cryptographic API. * * MD4 Message Digest Algorithm (RFC1320). * * Implementation derived from Andrew Tridgell and Steve French's * CIFS MD4 implementation, and the cryptoapi implementation * originally based on the public domain implementation written * by Colin Plumb in 1993. * * Copyright (c) Andrew Tridgell 1997-1998. * Modified by Steve French (sfrench@us.ibm.com) 2002 * Modified by Namjae Jeon (namjae.jeon@samsung.com) 2015 * Copyright (c) Cryptoapi developers. * Copyright (c) 2002 David S. Miller (davem@redhat.com) * Copyright (c) 2002 James Morris */ #include #include #include #include #define u8 unsigned char #define u32 unsigned int #define u64 unsigned long long #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static inline u32 lshift(u32 x, unsigned int s) { x &= 0xFFFFFFFF; return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s)); } static inline u32 F(u32 x, u32 y, u32 z) { return (x & y) | ((~x) & z); } static inline u32 G(u32 x, u32 y, u32 z) { return (x & y) | (x & z) | (y & z); } static inline u32 H(u32 x, u32 y, u32 z) { return x ^ y ^ z; } static inline void ROUND1(u32 *a, u32 b, u32 c, u32 d, u32 k, u32 s) { *a = lshift(*a + F(b, c, d) + k, s); } static inline void ROUND2(u32 *a, u32 b, u32 c, u32 d, u32 k, u32 s) { *a = lshift(*a + G(b, c, d) + k + (u32)0x5A827999, s); } static inline void ROUND3(u32 *a, u32 b, u32 c, u32 d, u32 k, u32 s) { *a = lshift(*a + H(b, c, d) + k + (u32)0x6ED9EBA1, s); } /* XXX: this stuff can be optimized */ static inline void le32_to_cpu_array(u32 *buf, unsigned int words) { while (words--) { __le32_to_cpus(buf); buf++; } } static inline void cpu_to_le32_array(u32 *buf, unsigned int words) { while (words--) { __cpu_to_le32s(buf); buf++; } } static void md4_transform(u32 *hash, u32 const *in) { u32 a, b, c, d; a = hash[0]; b = hash[1]; c = hash[2]; d = hash[3]; ROUND1(&a, b, c, d, in[0], 3); ROUND1(&d, a, b, c, in[1], 7); ROUND1(&c, d, a, b, in[2], 11); ROUND1(&b, c, d, a, in[3], 19); ROUND1(&a, b, c, d, in[4], 3); ROUND1(&d, a, b, c, in[5], 7); ROUND1(&c, d, a, b, in[6], 11); ROUND1(&b, c, d, a, in[7], 19); ROUND1(&a, b, c, d, in[8], 3); ROUND1(&d, a, b, c, in[9], 7); ROUND1(&c, d, a, b, in[10], 11); ROUND1(&b, c, d, a, in[11], 19); ROUND1(&a, b, c, d, in[12], 3); ROUND1(&d, a, b, c, in[13], 7); ROUND1(&c, d, a, b, in[14], 11); ROUND1(&b, c, d, a, in[15], 19); ROUND2(&a, b, c, d, in[0], 3); ROUND2(&d, a, b, c, in[4], 5); ROUND2(&c, d, a, b, in[8], 9); ROUND2(&b, c, d, a, in[12], 13); ROUND2(&a, b, c, d, in[1], 3); ROUND2(&d, a, b, c, in[5], 5); ROUND2(&c, d, a, b, in[9], 9); ROUND2(&b, c, d, a, in[13], 13); ROUND2(&a, b, c, d, in[2], 3); ROUND2(&d, a, b, c, in[6], 5); ROUND2(&c, d, a, b, in[10], 9); ROUND2(&b, c, d, a, in[14], 13); ROUND2(&a, b, c, d, in[3], 3); ROUND2(&d, a, b, c, in[7], 5); ROUND2(&c, d, a, b, in[11], 9); ROUND2(&b, c, d, a, in[15], 13); ROUND3(&a, b, c, d, in[0], 3); ROUND3(&d, a, b, c, in[8], 9); ROUND3(&c, d, a, b, in[4], 11); ROUND3(&b, c, d, a, in[12], 15); ROUND3(&a, b, c, d, in[2], 3); ROUND3(&d, a, b, c, in[10], 9); ROUND3(&c, d, a, b, in[6], 11); ROUND3(&b, c, d, a, in[14], 15); ROUND3(&a, b, c, d, in[1], 3); ROUND3(&d, a, b, c, in[9], 9); ROUND3(&c, d, a, b, in[5], 11); ROUND3(&b, c, d, a, in[13], 15); ROUND3(&a, b, c, d, in[3], 3); ROUND3(&d, a, b, c, in[11], 9); ROUND3(&c, d, a, b, in[7], 11); ROUND3(&b, c, d, a, in[15], 15); hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; } static inline void md4_transform_helper(struct md4_ctx *ctx) { le32_to_cpu_array(ctx->block, ARRAY_SIZE(ctx->block)); md4_transform(ctx->hash, ctx->block); } void md4_init(struct md4_ctx *mctx) { mctx->hash[0] = 0x67452301; mctx->hash[1] = 0xefcdab89; mctx->hash[2] = 0x98badcfe; mctx->hash[3] = 0x10325476; mctx->byte_count = 0; } void md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len) { const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); mctx->byte_count += len; if (avail > len) { memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), data, len); return; } memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), data, avail); md4_transform_helper(mctx); data += avail; len -= avail; while (len >= sizeof(mctx->block)) { memcpy(mctx->block, data, sizeof(mctx->block)); md4_transform_helper(mctx); data += sizeof(mctx->block); len -= sizeof(mctx->block); } memcpy(mctx->block, data, len); } void md4_final(struct md4_ctx *mctx, u8 *out) { const unsigned int offset = mctx->byte_count & 0x3f; char *p = (char *)mctx->block + offset; int padding = 56 - (offset + 1); *p++ = 0x80; if (padding < 0) { memset(p, 0x00, padding + sizeof(u64)); md4_transform_helper(mctx); p = (char *)mctx->block; padding = 56; } memset(p, 0, padding); mctx->block[14] = mctx->byte_count << 3; mctx->block[15] = mctx->byte_count >> 29; le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - sizeof(u64)) / sizeof(u32)); md4_transform(mctx->hash, mctx->block); cpu_to_le32_array(mctx->hash, ARRAY_SIZE(mctx->hash)); memcpy(out, mctx->hash, sizeof(mctx->hash)); memset(mctx, 0, sizeof(*mctx)); } ksmbd-tools-3.4.4/adduser/md4_hash.h000066400000000000000000000021141417226526000172520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Cryptographic API. * * MD4 Message Digest Algorithm (RFC1320). * * Implementation derived from Andrew Tridgell and Steve French's * CIFS MD4 implementation, and the cryptoapi implementation * originally based on the public domain implementation written * by Colin Plumb in 1993. * * Copyright (c) Andrew Tridgell 1997-1998. * Modified by Steve French (sfrench@us.ibm.com) 2002 * Modified by Namjae Jeon (namjae.jeon@samsung.com) 2015 * Copyright (c) Cryptoapi developers. * Copyright (c) 2002 David S. Miller (davem@redhat.com) * Copyright (c) 2002 James Morris */ #ifndef __MD4_HASH_H__ #define __MD4_HASH_H__ #define MD4_BLOCK_WORDS 16 #define MD4_HASH_WORDS 4 struct md4_ctx { unsigned int hash[MD4_HASH_WORDS]; unsigned int block[MD4_BLOCK_WORDS]; unsigned long long byte_count; }; void md4_init(struct md4_ctx *mctx); void md4_update(struct md4_ctx *mctx, const unsigned char *data, unsigned int len); void md4_final(struct md4_ctx *mctx, unsigned char *out); #endif /* __MD4_HASH_H__ */ ksmbd-tools-3.4.4/adduser/user_admin.c000066400000000000000000000170421417226526000177120ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_NT_PWD_LEN 129 static char *arg_account = NULL; static char *arg_password = NULL; static int conf_fd = -1; static char wbuf[2 * MAX_NT_PWD_LEN + 2 * KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; static int __opendb_file(char *pwddb) { conf_fd = open(pwddb, O_WRONLY); if (conf_fd == -1) { pr_err("%s %s\n", strerr(errno), pwddb); return -EINVAL; } if (ftruncate(conf_fd, 0)) { pr_err("%s %s\n", strerr(errno), pwddb); close(conf_fd); return -EINVAL; } return 0; } static void term_toggle_echo(int on_off) { struct termios term; tcgetattr(STDIN_FILENO, &term); if (on_off) term.c_lflag |= ECHO; else term.c_lflag &= ~ECHO; tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); } static char *__prompt_password_stdin(size_t *sz) { char *pswd1 = calloc(1, MAX_NT_PWD_LEN + 1); char *pswd2 = calloc(1, MAX_NT_PWD_LEN + 1); size_t len = 0; int i; if (!pswd1 || !pswd2) { free(pswd1); free(pswd2); pr_err("Out of memory\n"); return NULL; } again: printf("New password: "); term_toggle_echo(0); if (fgets(pswd1, MAX_NT_PWD_LEN, stdin) == NULL) { term_toggle_echo(1); pr_err("\nFatal error: %s\n", strerr(errno)); free(pswd1); free(pswd2); return NULL; } printf("\nRetype new password: "); if (fgets(pswd2, MAX_NT_PWD_LEN, stdin) == NULL) { term_toggle_echo(1); pr_err("\nFatal error: %s\n", strerr(errno)); free(pswd1); free(pswd2); return NULL; } term_toggle_echo(1); printf("\n"); len = strlen(pswd1); for (i = 0; i < len; i++) if (pswd1[i] == '\n') pswd1[i] = 0x00; len = strlen(pswd2); for (i = 0; i < len; i++) if (pswd2[i] == '\n') pswd2[i] = 0x00; if (memcmp(pswd1, pswd2, MAX_NT_PWD_LEN + 1)) { pr_err("Passwords don't match\n"); goto again; } len = strlen(pswd1); if (len <= 1) { pr_err("No password was provided\n"); goto again; } *sz = len; free(pswd2); return pswd1; } static char *prompt_password(size_t *sz) { if (!arg_password) return __prompt_password_stdin(sz); *sz = strlen(arg_password); return arg_password; } static char *get_utf8_password(long *len) { size_t raw_sz; char *pswd_raw, *pswd_converted; gsize bytes_read = 0; gsize bytes_written = 0; pswd_raw = prompt_password(&raw_sz); if (!pswd_raw) return NULL; pswd_converted = ksmbd_gconvert(pswd_raw, raw_sz, KSMBD_CHARSET_UTF16LE, KSMBD_CHARSET_DEFAULT, &bytes_read, &bytes_written); if (!pswd_converted) { free(pswd_raw); return NULL; } *len = bytes_written; free(pswd_raw); return pswd_converted; } static void __sanity_check(char *pswd_hash, char *pswd_b64) { size_t pass_sz; char *pass = base64_decode(pswd_b64, &pass_sz); if (!pass) { pr_err("Unable to decode NT hash\n"); exit(EXIT_FAILURE); } if (memcmp(pass, pswd_hash, pass_sz)) { pr_err("NT hash encoding error\n"); exit(EXIT_FAILURE); } free(pass); } static char *get_hashed_b64_password(void) { struct md4_ctx mctx; long len; char *pswd_plain, *pswd_hash, *pswd_b64; pswd_plain = get_utf8_password(&len); if (!pswd_plain) return NULL; pswd_hash = calloc(1, sizeof(mctx.hash) + 1); if (!pswd_hash) { free(pswd_plain); pr_err("Out of memory\n"); return NULL; } md4_init(&mctx); md4_update(&mctx, pswd_plain, len); md4_final(&mctx, pswd_hash); pswd_b64 = base64_encode(pswd_hash, MD4_HASH_WORDS * sizeof(unsigned int)); __sanity_check(pswd_hash, pswd_b64); free(pswd_plain); free(pswd_hash); return pswd_b64; } static void write_user(struct ksmbd_user *user) { int ret, nr = 0; size_t wsz; if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT)) return; wsz = snprintf(wbuf, sizeof(wbuf), "%s:%s\n", user->name, user->pass_b64); if (wsz > sizeof(wbuf)) { pr_err("Entry size is above the limit: %zu > %zu\n", wsz, sizeof(wbuf)); exit(EXIT_FAILURE); } while (wsz && (ret = write(conf_fd, wbuf + nr, wsz)) != 0) { if (ret == -1) { if (errno == EINTR) continue; pr_err("%s\n", strerr(errno)); exit(EXIT_FAILURE); } nr += ret; wsz -= ret; } } static void write_user_cb(gpointer key, gpointer value, gpointer user_data) { struct ksmbd_user *user = (struct ksmbd_user *)value; write_user(user); } static void write_remove_user_cb(gpointer key, gpointer value, gpointer user_data) { struct ksmbd_user *user = (struct ksmbd_user *)value; if (!g_ascii_strcasecmp(user->name, arg_account)) { pr_info("User '%s' removed\n", user->name); return; } write_user_cb(key, value, user_data); } static void lookup_can_del_user(gpointer key, gpointer value, gpointer user_data) { struct ksmbd_share *share = (struct ksmbd_share *)value; int ret = 0; int *abort_del_user = (int *)user_data; if (*abort_del_user) return; ret = shm_lookup_users_map(share, KSMBD_SHARE_ADMIN_USERS_MAP, arg_account); if (ret == 0) goto conflict; ret = shm_lookup_users_map(share, KSMBD_SHARE_WRITE_LIST_MAP, arg_account); if (ret == 0) goto conflict; ret = shm_lookup_users_map(share, KSMBD_SHARE_VALID_USERS_MAP, arg_account); if (ret == 0) goto conflict; *abort_del_user = 0; return; conflict: pr_err("Share %s requires user %s to exist\n", share->name, arg_account); *abort_del_user = 1; } int command_add_user(char *pwddb, char *account, char *password) { struct ksmbd_user *user; char *pswd; arg_account = account; arg_password = password; user = usm_lookup_user(arg_account); if (user) { put_ksmbd_user(user); pr_err("Account `%s' already exists\n", arg_account); return -EEXIST; } pswd = get_hashed_b64_password(); if (!pswd) { pr_err("Out of memory\n"); return -EINVAL; } /* pswd is already g_strdup-ed */ if (usm_add_new_user(arg_account, pswd)) { pr_err("Could not add new account\n"); return -EINVAL; } pr_info("User '%s' added\n", arg_account); if (__opendb_file(pwddb)) return -EINVAL; for_each_ksmbd_user(write_user_cb, NULL); close(conf_fd); return 0; } int command_update_user(char *pwddb, char *account, char *password) { struct ksmbd_user *user; char *pswd; arg_password = password; arg_account = account; user = usm_lookup_user(arg_account); if (!user) { pr_err("Unknown account\n"); return -EINVAL; } pswd = get_hashed_b64_password(); if (!pswd) { pr_err("Out of memory\n"); put_ksmbd_user(user); return -EINVAL; } if (usm_update_user_password(user, pswd)) { pr_err("Out of memory\n"); put_ksmbd_user(user); return -ENOMEM; } pr_info("User '%s' updated\n", account); put_ksmbd_user(user); free(pswd); if (__opendb_file(pwddb)) return -EINVAL; for_each_ksmbd_user(write_user_cb, NULL); close(conf_fd); return 0; } int command_del_user(char *pwddb, char *account) { int abort_del_user = 0; arg_account = account; if (!cp_key_cmp(global_conf.guest_account, arg_account)) { pr_err("User %s is a global guest account. Abort deletion.\n", arg_account); return -EINVAL; } for_each_ksmbd_share(lookup_can_del_user, &abort_del_user); if (abort_del_user) { pr_err("Aborting user deletion\n"); return -EINVAL; } if (__opendb_file(pwddb)) return -EINVAL; for_each_ksmbd_user(write_remove_user_cb, NULL); close(conf_fd); return 0; } ksmbd-tools-3.4.4/adduser/user_admin.h000066400000000000000000000006731417226526000177210ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __KSMBD_USER_ADMIN_H__ #define __KSMBD_USER_ADMIN_H__ int command_add_user(char *pwddb, char *account, char *password); int command_update_user(char *pwddb, char *account, char *password); int command_del_user(char *pwddb, char *account); #endif /* __KSMBD_USER_ADMIN_H__ */ ksmbd-tools-3.4.4/autogen.sh000077500000000000000000000000521417226526000157630ustar00rootroot00000000000000#!/bin/sh autoreconf --install --verbose ksmbd-tools-3.4.4/configure.ac000066400000000000000000000036121417226526000162550ustar00rootroot00000000000000AC_PREREQ([2.68]) m4_define([ksmbd_tools_version], m4_esyscmd_s( grep "define KSMBD_TOOLS_VERSION " include/version.h | \ awk '{print $3}' | sed 's/\"//g')) AC_INIT([ksmbd-tools], ksmbd_tools_version, [linkinjeon@kernel.org], [ksmbd-tools], [https://github.com/cifsd-team/ksmbd-tools]) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([foreign tar-pax subdir-objects]) AC_LANG([C]) AC_PROG_CC AC_PROG_CC_STDC AM_SILENT_RULES([yes]) AC_PROG_LIBTOOL # enable options AC_ARG_ENABLE(krb5, [AC_HELP_STRING([--enable-krb5], [Enable kerberos authentication @<:@default=no@:>@:])], enable_krb5=$enableval, enable_krb5="no") # Checks for header files. if test "$enable_krb5" != "no"; then AC_CHECK_HEADERS([krb5.h]) if test x$ac_cv_header_krb5_h != xyes; then if test "$enable_krb5" = "yes"; then AC_MSG_ERROR([krb5.h not found, please install krb5-libs-devel.]) else AC_MSG_WARN([krb5.h not found, disabling krb5.]) enable_krb5="no" fi fi fi PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.40], [has_glib2=1], [has_glib2=0]) AS_IF([test "$has_glib2" -eq 0], AC_MSG_ERROR([No glib2 (libglib2.0-dev or glib2-devel) was found]) ]) has_libnl_ver=0 dnl libnl-genl-3.0.pc pkg-config file just for libnl-3.0 case. PKG_CHECK_MODULES([LIBNL], [libnl-3.0 >= 3.0 libnl-genl-3.0 >= 3.0], [has_libnl_ver=3], [has_libnl_ver=0]) AS_IF([test "$has_libnl_ver" -eq 0], [ AC_MSG_ERROR([No libnl (libnl-3-dev or libnl3-devel) and libnl-genl (libnl-genl-3-dev) or were not found]) ]) if test "$enable_krb5" != "no"; then PKG_CHECK_MODULES([LIBKRB5], [krb5]) AC_DEFINE([CONFIG_KRB5], [], "support kerberos authentication") fi AM_CONDITIONAL(HAVE_LIBKRB5, [test "$enable_krb5" != "no"]) AC_CONFIG_FILES([ Makefile lib/Makefile mountd/Makefile adduser/Makefile addshare/Makefile control/Makefile ]) AC_OUTPUT ksmbd-tools-3.4.4/control/000077500000000000000000000000001417226526000154455ustar00rootroot00000000000000ksmbd-tools-3.4.4/control/Makefile.am000066400000000000000000000003371417226526000175040ustar00rootroot00000000000000AM_CFLAGS = -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common LIBS = $(GLIB_LIBS) ksmbd_control_LDADD = $(top_builddir)/lib/libksmbdtools.a sbin_PROGRAMS = ksmbd.control ksmbd_control_SOURCES = control.c ksmbd-tools-3.4.4/control/control.c000066400000000000000000000044551417226526000173010ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include "ksmbdtools.h" #include "version.h" static void usage(void) { fprintf(stderr, "Usage: ksmbd.control\n"); fprintf(stderr, "\t-s | --shutdown\n"); fprintf(stderr, "\t-d | --debug=all or [smb, auth, etc...]\n"); fprintf(stderr, "\t-c | --ksmbd-version\n"); fprintf(stderr, "\t-V | --version\n"); exit(EXIT_FAILURE); } static void show_version(void) { printf("ksmbd-tools version : %s\n", KSMBD_TOOLS_VERSION); exit(EXIT_FAILURE); } static int ksmbd_control_shutdown(void) { int fd, ret; terminate_ksmbd_daemon(); fd = open("/sys/class/ksmbd-control/kill_server", O_WRONLY); if (fd < 0) { pr_err("open failed: %d\n", errno); return EXIT_FAILURE; } ret = write(fd, "hard", 4); close(fd); return ret != -1 ? EXIT_SUCCESS : EXIT_FAILURE; } static int ksmbd_control_show_version(void) { int fd, ret; char ver[255] = {0}; fd = open("/sys/module/ksmbd/version", O_RDONLY); if (fd < 0) { pr_err("open failed: %d\n", errno); return EXIT_FAILURE; } ret = read(fd, ver, 255); close(fd); if (ret != -1) pr_info("ksmbd version : %s\n", ver); return ret != -1 ? EXIT_SUCCESS : EXIT_FAILURE; } static int ksmbd_control_debug(char *comp) { int fd, ret; char buf[255] = {0}; fd = open("/sys/class/ksmbd-control/debug", O_RDWR); if (fd < 0) { pr_err("open failed: %d\n", errno); return EXIT_FAILURE; } ret = write(fd, comp, strlen(comp)); if (ret < 0) goto out; ret = read(fd, buf, 255); if (ret < 0) goto out; pr_info("%s\n", buf); out: close(fd); return ret != -1 ? EXIT_SUCCESS : EXIT_FAILURE; } int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; int c; set_logger_app_name("ksmbd.control"); if (getuid() != 0) { pr_err("Please try it as root.\n"); return ret; } opterr = 0; while ((c = getopt(argc, argv, "sd:cVh")) != EOF) switch (c) { case 's': ret = ksmbd_control_shutdown(); break; case 'd': ret = ksmbd_control_debug(optarg); break; case 'c': ret = ksmbd_control_show_version(); break; case 'V': show_version(); break; case '?': case 'h': default: usage(); } if (argc < 2) usage(); return ret; } ksmbd-tools-3.4.4/include/000077500000000000000000000000001417226526000154105ustar00rootroot00000000000000ksmbd-tools-3.4.4/include/asn1.h000066400000000000000000000060161417226526000164260ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef ASN1_DECODER_H_ #define ASN1_DECODER_H_ /* Class */ #define ASN1_UNI 0 /* Universal */ #define ASN1_APL 1 /* Application */ #define ASN1_CTX 2 /* Context */ #define ASN1_PRV 3 /* Private */ /* Tag */ #define ASN1_EOC 0 /* End Of Contents or N/A */ #define ASN1_BOL 1 /* Boolean */ #define ASN1_INT 2 /* Integer */ #define ASN1_BTS 3 /* Bit String */ #define ASN1_OTS 4 /* Octet String */ #define ASN1_NUL 5 /* Null */ #define ASN1_OJI 6 /* Object Identifier */ #define ASN1_OJD 7 /* Object Description */ #define ASN1_EXT 8 /* External */ #define ASN1_ENUM 10 /* Enumerated */ #define ASN1_SEQ 16 /* Sequence */ #define ASN1_SET 17 /* Set */ #define ASN1_NUMSTR 18 /* Numerical String */ #define ASN1_PRNSTR 19 /* Printable String */ #define ASN1_TEXSTR 20 /* Teletext String */ #define ASN1_VIDSTR 21 /* Video String */ #define ASN1_IA5STR 22 /* IA5 String */ #define ASN1_UNITIM 23 /* Universal Time */ #define ASN1_GENTIM 24 /* General Time */ #define ASN1_GRASTR 25 /* Graphical String */ #define ASN1_VISSTR 26 /* Visible String */ #define ASN1_GENSTR 27 /* General String */ /* Primitive / Constructed methods*/ #define ASN1_PRI 0 /* Primitive */ #define ASN1_CON 1 /* Constructed */ /* * Error codes. */ #define ASN1_ERR_NOERROR 0 #define ASN1_ERR_DEC_EMPTY 2 #define ASN1_ERR_DEC_EOC_MISMATCH 3 #define ASN1_ERR_DEC_LENGTH_MISMATCH 4 #define ASN1_ERR_DEC_BADVALUE 5 #define SPNEGO_OID_LEN 7 #define NTLMSSP_OID_LEN 10 #define KRB5_OID_LEN 7 #define KRB5U2U_OID_LEN 8 #define MSKRB5_OID_LEN 7 static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 }; static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 }; static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 }; static unsigned long KRB5U2U_OID[8] = { 1, 2, 840, 113554, 1, 2, 2, 3 }; static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 }; /* * ASN.1 context. */ struct asn1_ctx { int error; /* Error condition */ unsigned char *pointer; /* Octet just to be decoded */ unsigned char *begin; /* First octet */ unsigned char *end; /* Octet after last octet */ }; /* * Octet string (not null terminated) */ struct asn1_octstr { unsigned char *data; unsigned int len; }; void asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len); unsigned char asn1_header_decode(struct asn1_ctx *ctx, unsigned char **eoc, unsigned int *cls, unsigned int *con, unsigned int *tag); unsigned char asn1_octets_decode(struct asn1_ctx *ctx, unsigned char *eoc, unsigned char **octets, unsigned int *len); unsigned char asn1_read(struct asn1_ctx *ctx, unsigned char **buf, unsigned int len); int asn1_oid_decode(struct asn1_ctx *ctx, unsigned char *eoc, unsigned long **oid, unsigned int *len); int asn1_header_len(unsigned int payload_len, int depth); int asn1_header_encode(unsigned char **buf, unsigned int cls, unsigned int con, unsigned int tag, unsigned int *len); int asn1_oid_encode(const unsigned long *in_oid, int in_len, unsigned char **out_oid, int *out_len); #endif ksmbd-tools-3.4.4/include/config_parser.h000066400000000000000000000021521417226526000204020ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __KSMBD_CONFIG_H__ #define __KSMBD_CONFIG_H__ #include struct smbconf_group { char *name; GHashTable *kv; }; struct smbconf_parser { GHashTable *groups; struct smbconf_group *current; }; extern struct smbconf_parser parser; int cp_parse_external_smbconf_group(char *name, char *opts); int cp_smbconfig_hash_create(const char *smbconf); void cp_smbconfig_destroy(void); int cp_parse_pwddb(const char *pwddb); int cp_parse_smbconf(const char *smbconf); int cp_parse_reload_smbconf(const char *smbconf); int cp_parse_subauth(const char *subauth_path); char *cp_ltrim(char *v); int cp_key_cmp(char *k, char *v); char *cp_get_group_kv_string(char *v); int cp_get_group_kv_bool(char *v); unsigned long cp_get_group_kv_long_base(char *v, int base); unsigned long cp_get_group_kv_long(char *v); int cp_get_group_kv_config_opt(char *v); char **cp_get_group_kv_list(char *v); void cp_group_kv_list_free(char **list); #endif /* __KSMBD_CONFIG_H__ */ ksmbd-tools-3.4.4/include/ipc.h000066400000000000000000000015101417226526000163310ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __KSMBD_IPC_H__ #define __KSMBD_IPC_H__ /* * Older [prior to 4.9] kernels had max NL recv msg size of 16k. * It has been bumped to 32K later on. */ #define KSMBD_IPC_MAX_MESSAGE_SIZE (16 * 1024) struct ksmbd_ipc_msg { unsigned int type; unsigned int sz; unsigned char ____payload[0]; }; #define KSMBD_IPC_MSG_PAYLOAD(m) \ (void *)(((struct ksmbd_ipc_msg *)(m))->____payload) #define KSMBD_STATUS_IPC_FATAL_ERROR 11 struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz); void ipc_msg_free(struct ksmbd_ipc_msg *msg); int ipc_msg_send(struct ksmbd_ipc_msg *msg); int ipc_process_event(void); void ipc_destroy(void); int ipc_init(void); #endif /* __KSMBD_IPC_H__ */ ksmbd-tools-3.4.4/include/ksmbdtools.h000066400000000000000000000104241417226526000177430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __KSMBDTOOLS_H__ #define __KSMBDTOOLS_H__ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif struct smbconf_global { int flags; int map_to_guest; char *guest_account; char *server_string; char *work_group; char *netbios_name; char *server_min_protocol; char *server_max_protocol; char *root_dir; int server_signing; int sessions_cap; int restrict_anon; unsigned short tcp_port; unsigned short ipc_timeout; unsigned int deadtime; int bind_interfaces_only; char **interfaces; unsigned long file_max; unsigned int smb2_max_read; unsigned int smb2_max_write; unsigned int smb2_max_trans; unsigned int smb2_max_credits; unsigned int share_fake_fscaps; unsigned int gen_subauth[3]; char *krb5_keytab_file; char *krb5_service_name; char *pwddb; char *smbconf; }; #define KSMBD_LOCK_FILE "/tmp/ksmbd.lock" #define KSMBD_RESTRICT_ANON_TYPE_1 1 #define KSMBD_RESTRICT_ANON_TYPE_2 2 extern struct smbconf_global global_conf; #define KSMBD_CONF_MAP_TO_GUEST_NEVER (0) #define KSMBD_CONF_MAP_TO_GUEST_BAD_USER (1 << 0) #define KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD (1 << 1) #define KSMBD_CONF_MAP_TO_GUEST_BAD_UID (1 << 2) #define KSMBD_CONF_DEFAULT_NETBIOS_NAME "KSMBD SERVER" #define KSMBD_CONF_DEFAULT_SERVER_STRING "SMB SERVER" #define KSMBD_CONF_DEFAULT_WORK_GROUP "WORKGROUP" #define KSMBD_CONF_DEFAULT_GUEST_ACCOUNT "nobody" #define KSMBD_CONF_FALLBACK_GUEST_ACCOUNT "ftp" #define KSMBD_CONF_DEFAULT_SESS_CAP 1024 #define KSMBD_CONF_DEFAULT_TPC_PORT 445 #define KSMBD_CONF_FILE_MAX 10000 #define PATH_PWDDB "/etc/ksmbd/ksmbdpwd.db" #define PATH_SMBCONF "/etc/ksmbd/smb.conf" #define KSMBD_HEALTH_START (0) #define KSMBD_HEALTH_RUNNING (1 << 0) #define KSMBD_SHOULD_RELOAD_CONFIG (1 << 1) extern int ksmbd_health_status; #define TRACING_DUMP_NL_MSG 0 #define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) //---------------------------------------------------------------// #define LOGAPP "[%s/%d]:" #define PRERR LOGAPP" ERROR: " #define PRINF LOGAPP" INFO: " #define PRDEBUG LOGAPP" DEBUG: " #define PR_ERROR 0 #define PR_INFO 1 #define PR_DEBUG 2 static int log_level = PR_INFO; #define PR_LOGGER_STDIO 0 #define PR_LOGGER_SYSLOG 1 /* * A thread-safe strerror() wrapper, uses static TLS buffer. * NOTE: a signal handler can concurrent modify the buffer, * but the buffer should always be nul-terminated. */ char *strerr(int err); __attribute__ ((format (printf, 2, 3))) extern void __pr_log(int level, const char *fmt, ...); extern void set_logger_app_name(const char *an); extern const char *get_logger_app_name(void); extern void pr_logger_init(int flags); #define pr_log(l, f, ...) \ do { \ if ((l) <= log_level) \ __pr_log((l), (f), get_logger_app_name(), \ getpid(), \ ##__VA_ARGS__); \ } while (0) #define pr_debug(f, ...) \ pr_log(PR_DEBUG, PRDEBUG f, ##__VA_ARGS__) #define pr_info(f, ...) \ pr_log(PR_INFO, PRINF f, ##__VA_ARGS__) #define pr_err(f, ...) \ pr_log(PR_ERROR, PRERR f, ##__VA_ARGS__) //---------------------------------------------------------------// void pr_hex_dump(const void *mem, size_t sz); char *base64_encode(unsigned char *src, size_t srclen); unsigned char *base64_decode(char const *src, size_t *dstlen); gchar *ksmbd_gconvert(const gchar *str, gssize str_len, int to_codeset, int from_codeset, gsize *bytes_read, gsize *bytes_written); enum charset_idx { KSMBD_CHARSET_UTF8 = 0, KSMBD_CHARSET_UTF16LE, KSMBD_CHARSET_UCS2LE, KSMBD_CHARSET_UTF16BE, KSMBD_CHARSET_UCS2BE, KSMBD_CHARSET_MAX = 5, }; #define KSMBD_CHARSET_DEFAULT KSMBD_CHARSET_UTF8 extern char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1]; void notify_ksmbd_daemon(void); void terminate_ksmbd_daemon(void); int test_file_access(char *conf); #endif /* __KSMBDTOOLS_H__ */ ksmbd-tools-3.4.4/include/linux/000077500000000000000000000000001417226526000165475ustar00rootroot00000000000000ksmbd-tools-3.4.4/include/linux/ksmbd_server.h000066400000000000000000000165251417226526000214170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-ksmbd-devel@lists.sourceforge.net */ #ifndef _LINUX_KSMBD_SERVER_H #define _LINUX_KSMBD_SERVER_H #include #define KSMBD_GENL_NAME "SMBD_GENL" #define KSMBD_GENL_VERSION 0x01 #define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48 #define KSMBD_REQ_MAX_HASH_SZ 18 #define KSMBD_REQ_MAX_SHARE_NAME 64 struct ksmbd_heartbeat { __u32 handle; }; /* * Global config flags. */ #define KSMBD_GLOBAL_FLAG_INVALID (0) #define KSMBD_GLOBAL_FLAG_SMB2_LEASES (1 << 0) #define KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION (1 << 1) #define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL (1 << 2) struct ksmbd_startup_request { __u32 flags; __s32 signing; __s8 min_prot[16]; __s8 max_prot[16]; __s8 netbios_name[16]; __s8 work_group[64]; __s8 server_string[64]; __u16 tcp_port; __u16 ipc_timeout; __u32 deadtime; __u32 file_max; __u32 smb2_max_write; __u32 smb2_max_read; __u32 smb2_max_trans; __u32 share_fake_fscaps; __u32 sub_auth[3]; __u32 smb2_max_credits; __u32 reserved[128]; /* Reserved room */ __u32 ifc_list_sz; __s8 ____payload[]; }; #define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) struct ksmbd_shutdown_request { __s32 reserved[16]; }; struct ksmbd_login_request { __u32 handle; __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; __u32 reserved[16]; /* Reserved room */ }; struct ksmbd_login_response { __u32 handle; __u32 gid; __u32 uid; __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; __u16 status; __u16 hash_sz; __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; __u32 reserved[16]; /* Reserved room */ }; struct ksmbd_share_config_request { __u32 handle; __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; __u32 reserved[16]; /* Reserved room */ }; struct ksmbd_share_config_response { __u32 handle; __u32 flags; __u16 create_mask; __u16 directory_mask; __u16 force_create_mode; __u16 force_directory_mode; __u16 force_uid; __u16 force_gid; __u32 reserved[128]; /* Reserved room */ __u32 veto_list_sz; __s8 ____payload[]; }; #define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) #define KSMBD_SHARE_CONFIG_PATH(s) \ ({ \ char *p = (s)->____payload; \ if ((s)->veto_list_sz) \ p += (s)->veto_list_sz + 1; \ p; \ }) struct ksmbd_tree_connect_request { __u32 handle; __u16 account_flags; __u16 flags; __u64 session_id; __u64 connect_id; __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; __s8 share[KSMBD_REQ_MAX_SHARE_NAME]; __s8 peer_addr[64]; __u32 reserved[16]; /* Reserved room */ }; struct ksmbd_tree_connect_response { __u32 handle; __u16 status; __u16 connection_flags; __u32 reserved[16]; /* Reserved room */ }; struct ksmbd_tree_disconnect_request { __u64 session_id; __u64 connect_id; __u32 reserved[16]; /* Reserved room */ }; struct ksmbd_logout_request { __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; __u32 account_flags; __u32 reserved[16]; /* Reserved room */ }; struct ksmbd_rpc_command { __u32 handle; __u32 flags; __u32 payload_sz; __u8 payload[]; }; struct ksmbd_spnego_authen_request { __u32 handle; __u16 spnego_blob_len; __u8 spnego_blob[]; }; struct ksmbd_spnego_authen_response { __u32 handle; struct ksmbd_login_response login_response; __u16 session_key_len; __u16 spnego_blob_len; __u8 payload[]; }; /* * This also used as NETLINK attribute type value. * * NOTE: * Response message type value should be equal to * request message type value + 1. */ enum ksmbd_event { KSMBD_EVENT_UNSPEC = 0, KSMBD_EVENT_HEARTBEAT_REQUEST, KSMBD_EVENT_STARTING_UP, KSMBD_EVENT_SHUTTING_DOWN, KSMBD_EVENT_LOGIN_REQUEST, KSMBD_EVENT_LOGIN_RESPONSE = 5, KSMBD_EVENT_SHARE_CONFIG_REQUEST, KSMBD_EVENT_SHARE_CONFIG_RESPONSE, KSMBD_EVENT_TREE_CONNECT_REQUEST, KSMBD_EVENT_TREE_CONNECT_RESPONSE, KSMBD_EVENT_TREE_DISCONNECT_REQUEST = 10, KSMBD_EVENT_LOGOUT_REQUEST, KSMBD_EVENT_RPC_REQUEST, KSMBD_EVENT_RPC_RESPONSE, KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, KSMBD_EVENT_MAX }; enum KSMBD_TREE_CONN_STATUS { KSMBD_TREE_CONN_STATUS_OK = 0, KSMBD_TREE_CONN_STATUS_NOMEM, KSMBD_TREE_CONN_STATUS_NO_SHARE, KSMBD_TREE_CONN_STATUS_NO_USER, KSMBD_TREE_CONN_STATUS_INVALID_USER, KSMBD_TREE_CONN_STATUS_HOST_DENIED = 5, KSMBD_TREE_CONN_STATUS_CONN_EXIST, KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS, KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS, KSMBD_TREE_CONN_STATUS_ERROR, }; /* * User config flags. */ #define KSMBD_USER_FLAG_INVALID (0) #define KSMBD_USER_FLAG_OK (1 << 0) #define KSMBD_USER_FLAG_BAD_PASSWORD (1 << 1) #define KSMBD_USER_FLAG_BAD_UID (1 << 2) #define KSMBD_USER_FLAG_BAD_USER (1 << 3) #define KSMBD_USER_FLAG_GUEST_ACCOUNT (1 << 4) #define KSMBD_USER_FLAG_DELAY_SESSION (1 << 5) /* * Share config flags. */ #define KSMBD_SHARE_FLAG_INVALID (0) #define KSMBD_SHARE_FLAG_AVAILABLE (1 << 0) #define KSMBD_SHARE_FLAG_BROWSEABLE (1 << 1) #define KSMBD_SHARE_FLAG_WRITEABLE (1 << 2) #define KSMBD_SHARE_FLAG_READONLY (1 << 3) #define KSMBD_SHARE_FLAG_GUEST_OK (1 << 4) #define KSMBD_SHARE_FLAG_GUEST_ONLY (1 << 5) #define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS (1 << 6) #define KSMBD_SHARE_FLAG_OPLOCKS (1 << 7) #define KSMBD_SHARE_FLAG_PIPE (1 << 8) #define KSMBD_SHARE_FLAG_HIDE_DOT_FILES (1 << 9) #define KSMBD_SHARE_FLAG_INHERIT_OWNER (1 << 10) #define KSMBD_SHARE_FLAG_STREAMS (1 << 11) #define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS (1 << 12) #define KSMBD_SHARE_FLAG_ACL_XATTR (1 << 13) /* * Tree connect request flags. */ #define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0) #define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 (1 << 0) #define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 (1 << 1) /* * Tree connect flags. */ #define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT (1 << 0) #define KSMBD_TREE_CONN_FLAG_READ_ONLY (1 << 1) #define KSMBD_TREE_CONN_FLAG_WRITABLE (1 << 2) #define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT (1 << 3) /* * RPC over IPC. */ #define KSMBD_RPC_METHOD_RETURN (1 << 0) #define KSMBD_RPC_SRVSVC_METHOD_INVOKE (1 << 1) #define KSMBD_RPC_SRVSVC_METHOD_RETURN ((1 << 1) | KSMBD_RPC_METHOD_RETURN) #define KSMBD_RPC_WKSSVC_METHOD_INVOKE (1 << 2) #define KSMBD_RPC_WKSSVC_METHOD_RETURN ((1 << 2) | KSMBD_RPC_METHOD_RETURN) #define KSMBD_RPC_IOCTL_METHOD ((1 << 3) | KSMBD_RPC_METHOD_RETURN) #define KSMBD_RPC_OPEN_METHOD (1 << 4) #define KSMBD_RPC_WRITE_METHOD (1 << 5) #define KSMBD_RPC_READ_METHOD ((1 << 6) | KSMBD_RPC_METHOD_RETURN) #define KSMBD_RPC_CLOSE_METHOD (1 << 7) #define KSMBD_RPC_RAP_METHOD ((1 << 8) | KSMBD_RPC_METHOD_RETURN) #define KSMBD_RPC_RESTRICTED_CONTEXT (1 << 9) #define KSMBD_RPC_SAMR_METHOD_INVOKE (1 << 10) #define KSMBD_RPC_SAMR_METHOD_RETURN ((1 << 10) | KSMBD_RPC_METHOD_RETURN) #define KSMBD_RPC_LSARPC_METHOD_INVOKE (1 << 11) #define KSMBD_RPC_LSARPC_METHOD_RETURN ((1 << 11) | KSMBD_RPC_METHOD_RETURN) #define KSMBD_RPC_OK 0 #define KSMBD_RPC_EBAD_FUNC 0x00000001 #define KSMBD_RPC_EACCESS_DENIED 0x00000005 #define KSMBD_RPC_EBAD_FID 0x00000006 #define KSMBD_RPC_ENOMEM 0x00000008 #define KSMBD_RPC_EBAD_DATA 0x0000000D #define KSMBD_RPC_ENOTIMPLEMENTED 0x00000040 #define KSMBD_RPC_EINVALID_PARAMETER 0x00000057 #define KSMBD_RPC_EMORE_DATA 0x000000EA #define KSMBD_RPC_EINVALID_LEVEL 0x0000007C #define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107 #define KSMBD_CONFIG_OPT_DISABLED 0 #define KSMBD_CONFIG_OPT_ENABLED 1 #define KSMBD_CONFIG_OPT_AUTO 2 #define KSMBD_CONFIG_OPT_MANDATORY 3 #endif /* _LINUX_KSMBD_SERVER_H */ ksmbd-tools-3.4.4/include/management/000077500000000000000000000000001417226526000175245ustar00rootroot00000000000000ksmbd-tools-3.4.4/include/management/session.h000066400000000000000000000014501417226526000213600ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __MANAGEMENT_TCONNECTION_H__ #define __MANAGEMENT_TCONNECTION_H__ #include struct ksmbd_user; struct ksmbd_session { unsigned long long id; struct ksmbd_user *user; GRWLock update_lock; GList *tree_conns; int ref_counter; }; struct ksmbd_tree_conn; int sm_check_sessions_capacity(unsigned long long id); int sm_handle_tree_connect(unsigned long long id, struct ksmbd_user *user, struct ksmbd_tree_conn *tree_conn); int sm_handle_tree_disconnect(unsigned long long sess_id, unsigned long long tree_conn_id); void sm_destroy(void); int sm_init(void); #endif /* __MANAGEMENT_TCONNECTION_H__ */ ksmbd-tools-3.4.4/include/management/share.h000066400000000000000000000102331417226526000207760ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __MANAGEMENT_SHARE_H__ #define __MANAGEMENT_SHARE_H__ #include enum share_users { /* Admin users */ KSMBD_SHARE_ADMIN_USERS_MAP = 0, /* Valid users */ KSMBD_SHARE_VALID_USERS_MAP, /* Invalid users */ KSMBD_SHARE_INVALID_USERS_MAP, /* Read-only users */ KSMBD_SHARE_READ_LIST_MAP, /* Read/Write access to a read-only share */ KSMBD_SHARE_WRITE_LIST_MAP, KSMBD_SHARE_USERS_MAX, }; enum share_hosts { KSMBD_SHARE_HOSTS_ALLOW_MAP = 0, KSMBD_SHARE_HOSTS_DENY_MAP, KSMBD_SHARE_HOSTS_MAX, }; #define KSMBD_SHARE_DEFAULT_CREATE_MASK 0744 #define KSMBD_SHARE_DEFAULT_DIRECTORY_MASK 0755 #define KSMBD_SHARE_INVALID_UID ((__u16)-1) #define KSMBD_SHARE_INVALID_GID ((__u16)-1) struct ksmbd_share { char *name; char *path; int max_connections; int num_connections; GRWLock update_lock; int ref_count; unsigned short create_mask; unsigned short directory_mask; unsigned short force_create_mode; unsigned short force_directory_mode; unsigned short force_uid; unsigned short force_gid; int flags; int state; char *veto_list; int veto_list_sz; char *guest_account; GHashTable *maps[KSMBD_SHARE_USERS_MAX]; /* * FIXME * We need to support IP ranges, netmasks, etc. * This is just a silly hostname matching, hence * these two are not in ->maps[]. */ GHashTable *hosts_allow_map; /* Deny access */ GHashTable *hosts_deny_map; /* One lock to rule them all [as of now] */ GRWLock maps_lock; char *comment; }; /* * WARNING: * * Add new entries only before to the bottom, right before * KSMBD_SHARE_CONF_MAX. See SHARE_CONF comment. * */ enum KSMBD_SHARE_CONF { KSMBD_SHARE_CONF_COMMENT = 0, KSMBD_SHARE_CONF_PATH, KSMBD_SHARE_CONF_GUEST_OK, KSMBD_SHARE_CONF_GUEST_ACCOUNT, KSMBD_SHARE_CONF_READ_ONLY, KSMBD_SHARE_CONF_BROWSEABLE = 5, KSMBD_SHARE_CONF_WRITE_OK, KSMBD_SHARE_CONF_WRITEABLE, KSMBD_SHARE_CONF_STORE_DOS_ATTRIBUTES, KSMBD_SHARE_CONF_OPLOCKS, KSMBD_SHARE_CONF_CREATE_MASK = 10, KSMBD_SHARE_CONF_DIRECTORY_MASK, KSMBD_SHARE_CONF_FORCE_CREATE_MODE, KSMBD_SHARE_CONF_FORCE_DIRECTORY_MODE, KSMBD_SHARE_CONF_FORCE_GROUP, KSMBD_SHARE_CONF_FORCE_USER = 15, KSMBD_SHARE_CONF_HIDE_DOT_FILES, KSMBD_SHARE_CONF_VALID_USERS, KSMBD_SHARE_CONF_INVALID_USERS, KSMBD_SHARE_CONF_READ_LIST, KSMBD_SHARE_CONF_WRITE_LIST = 20, KSMBD_SHARE_CONF_ADMIN_USERS, KSMBD_SHARE_CONF_HOSTS_ALLOW, KSMBD_SHARE_CONF_HOSTS_DENY, KSMBD_SHARE_CONF_MAX_CONNECTIONS, KSMBD_SHARE_CONF_VETO_FILES = 25, KSMBD_SHARE_CONF_INHERIT_OWNER, KSMBD_SHARE_CONF_FOLLOW_SYMLINKS, KSMBD_SHARE_CONF_VFS_OBJECTS, KSMBD_SHARE_CONF_WRITABLE, KSMBD_SHARE_CONF_MAX }; extern char *KSMBD_SHARE_CONF[KSMBD_SHARE_CONF_MAX]; int shm_share_config(char *k, enum KSMBD_SHARE_CONF c); static inline void set_share_flag(struct ksmbd_share *share, int flag) { share->flags |= flag; } static inline void clear_share_flag(struct ksmbd_share *share, int flag) { share->flags &= ~flag; } static inline int test_share_flag(struct ksmbd_share *share, int flag) { return share->flags & flag; } struct ksmbd_share *get_ksmbd_share(struct ksmbd_share *share); void put_ksmbd_share(struct ksmbd_share *share); struct ksmbd_share *shm_lookup_share(char *name); struct smbconf_group; int shm_add_new_share(struct smbconf_group *group); void shm_remove_all_shares(void); void shm_destroy(void); int shm_init(void); int shm_lookup_users_map(struct ksmbd_share *share, enum share_users map, char *name); int shm_lookup_hosts_map(struct ksmbd_share *share, enum share_hosts map, char *host); int shm_open_connection(struct ksmbd_share *share); int shm_close_connection(struct ksmbd_share *share); typedef void (*walk_shares)(gpointer key, gpointer value, gpointer user_data); void for_each_ksmbd_share(walk_shares cb, gpointer user_data); struct ksmbd_share_config_response; int shm_share_config_payload_size(struct ksmbd_share *share); int shm_handle_share_config_request(struct ksmbd_share *share, struct ksmbd_share_config_response *resp); #endif /* __MANAGEMENT_SHARE_H__ */ ksmbd-tools-3.4.4/include/management/spnego.h000066400000000000000000000014601417226526000211710ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2020 LG Electronics * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef _MANAGEMENT_SPNEGO_H_ #define _MANAGEMENT_SPNEGO_H_ struct ksmbd_spnego_auth_out { char *spnego_blob; unsigned int blob_len; char *sess_key; unsigned int key_len; char *user_name; }; struct ksmbd_spnego_authen_request; #ifdef CONFIG_KRB5 int spnego_init(void); void spnego_destroy(void); int spnego_handle_authen_request(struct ksmbd_spnego_authen_request *req, struct ksmbd_spnego_auth_out *auth_out); #else static int spnego_init(void) { return 0; } static void spnego_destroy(void) {} static int spnego_handle_authen_request(struct ksmbd_spnego_authen_request *req, struct ksmbd_spnego_auth_out *auth_out) { return -ENOTSUP; } #endif #endif ksmbd-tools-3.4.4/include/management/tree_conn.h000066400000000000000000000020521417226526000216500ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __MANAGEMENT_TREE_CONN_H__ #define __MANAGEMENT_TREE_CONN_H__ #include struct ksmbd_share; struct ksmbd_tree_conn { unsigned long long id; struct ksmbd_share *share; unsigned int flags; }; static inline void set_conn_flag(struct ksmbd_tree_conn *conn, int bit) { conn->flags |= bit; } static inline void clear_conn_flag(struct ksmbd_tree_conn *conn, int bit) { conn->flags &= ~bit; } static inline int test_conn_flag(struct ksmbd_tree_conn *conn, int bit) { return conn->flags & bit; } void tcm_tree_conn_free(struct ksmbd_tree_conn *conn); struct ksmbd_tree_connect_request; struct ksmbd_tree_connect_response; int tcm_handle_tree_connect(struct ksmbd_tree_connect_request *req, struct ksmbd_tree_connect_response *resp); int tcm_handle_tree_disconnect(unsigned long long sess_id, unsigned long long tree_conn_id); #endif /* __MANAGEMENT_TREE_CONN_H__ */ ksmbd-tools-3.4.4/include/management/user.h000066400000000000000000000030471417226526000206570ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __MANAGEMENT_USER_H__ #define __MANAGEMENT_USER_H__ #include #include #include struct ksmbd_user { char *name; char *pass_b64; char *pass; int pass_sz; uid_t uid; gid_t gid; int ref_count; int flags; int state; GRWLock update_lock; unsigned int failed_login_count; }; static inline void set_user_flag(struct ksmbd_user *user, int bit) { user->flags |= bit; } static inline int test_user_flag(struct ksmbd_user *user, int bit) { return user->flags & bit; } struct ksmbd_user *get_ksmbd_user(struct ksmbd_user *user); void put_ksmbd_user(struct ksmbd_user *user); struct ksmbd_user *usm_lookup_user(char *name); int usm_update_user_password(struct ksmbd_user *user, char *pass); int usm_add_new_user(char *name, char *pwd); int usm_add_update_user_from_pwdentry(char *data); int usm_add_subauth_global_conf(char *data); void usm_remove_all_users(void); void usm_destroy(void); int usm_init(void); typedef void (*walk_users)(gpointer key, gpointer value, gpointer user_data); void for_each_ksmbd_user(walk_users cb, gpointer user_data); struct ksmbd_login_request; struct ksmbd_login_response; struct ksmbd_logout_request; int usm_handle_login_request(struct ksmbd_login_request *req, struct ksmbd_login_response *resp); int usm_handle_logout_request(struct ksmbd_logout_request *req); #endif /* __MANAGEMENT_USER_H__ */ ksmbd-tools-3.4.4/include/rpc.h000066400000000000000000000241021417226526000163440ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __KSMBD_RPC_H__ #define __KSMBD_RPC_H__ #include #include #define KSMBD_DCERPC_LITTLE_ENDIAN (1 << 0) #define KSMBD_DCERPC_ALIGN2 (1 << 1) #define KSMBD_DCERPC_ALIGN4 (1 << 2) #define KSMBD_DCERPC_ALIGN8 (1 << 3) #define KSMBD_DCERPC_ASCII_STRING (1 << 4) #define KSMBD_DCERPC_FIXED_PAYLOAD_SZ (1 << 5) #define KSMBD_DCERPC_EXTERNAL_PAYLOAD (1 << 6) #define KSMBD_DCERPC_RETURN_READY (1 << 7) #define KSMBD_DCERPC_MAX_PREFERRED_SIZE -1 #define DCERPC_PTYPE_RPC_REQUEST 0x00 #define DCERPC_PTYPE_RPC_PING 0x01 #define DCERPC_PTYPE_RPC_RESPONSE 0x02 #define DCERPC_PTYPE_RPC_FAULT 0x03 #define DCERPC_PTYPE_RPC_WORKING 0x04 #define DCERPC_PTYPE_RPC_NOCALL 0x05 #define DCERPC_PTYPE_RPC_REJECT 0x06 #define DCERPC_PTYPE_RPC_ACK 0x07 #define DCERPC_PTYPE_RPC_CL_CANCEL 0x08 #define DCERPC_PTYPE_RPC_FACK 0x09 #define DCERPC_PTYPE_RPC_CANCEL_ACK 0x0A #define DCERPC_PTYPE_RPC_BIND 0x0B #define DCERPC_PTYPE_RPC_BINDACK 0x0C #define DCERPC_PTYPE_RPC_BINDNACK 0x0D #define DCERPC_PTYPE_RPC_ALTCONT 0x0E #define DCERPC_PTYPE_RPC_ALTCONTRESP 0x0F #define DCERPC_PTYPE_RPC_AUTH3 0x10 #define DCERPC_PTYPE_RPC_SHUTDOWN 0x11 #define DCERPC_PTYPE_RPC_CO_CANCEL 0x12 #define DCERPC_PTYPE_RPC_ORPHANED 0x13 /* First fragment */ #define DCERPC_PFC_FIRST_FRAG 0x01 /* Last fragment */ #define DCERPC_PFC_LAST_FRAG 0x02 /* Cancel was pending at sender */ #define DCERPC_PFC_PENDING_CANCEL 0x04 #define DCERPC_PFC_RESERVED_1 0x08 /* Supports concurrent multiplexing of a single connection. */ #define DCERPC_PFC_CONC_MPX 0x10 /* * Only meaningful on `fault' packet; if true, guaranteed * call did not execute. */ #define DCERPC_PFC_DID_NOT_EXECUTE 0x20 /* `maybe' call semantics requested */ #define DCERPC_PFC_MAYBE 0x40 /* * If true, a non-nil object UUID was specified in the handle, and * is present in the optional object field. If false, the object field * is omitted. */ #define DCERPC_PFC_OBJECT_UUID 0x80 #define DCERPC_SERIALIZATION_TYPE1 1 #define DCERPC_SERIALIZATION_TYPE2 2 #define DCERPC_SERIALIZATION_LITTLE_ENDIAN 0x10 #define DCERPC_SERIALIZATION_BIG_ENDIAN 0x00 struct dcerpc_header { /* start 8-octet aligned */ /* common fields */ __u8 rpc_vers; /* 00:01 RPC version */ __u8 rpc_vers_minor; /* 01:01 minor version */ __u8 ptype; /* 02:01 bind PDU */ __u8 pfc_flags; /* 03:01 flags */ __s8 packed_drep[4]; /* 04:04 NDR data rep format label*/ __u16 frag_length; /* 08:02 total length of fragment */ __u16 auth_length; /* 10:02 length of auth_value */ __u32 call_id; /* 12:04 call identifier */ /* end common fields */ }; struct dcerpc_request_header { __u32 alloc_hint; __u16 context_id; __u16 opnum; /* * SWITCH dcerpc_object object; * PAYLOAD_BLOB; */ }; struct dcerpc_response_header { __u32 alloc_hint; __u16 context_id; __u8 cancel_count; }; /* * http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm * * We refer to pointers that are parameters in remote procedure calls as * top-level pointers and we refer to pointers that are elements of arrays, * members of structures, or members of unions as embedded pointers. * * NDR represents a null full pointer as an unsigned long integer with the * value 0 (zero). * NDR represents the first instance in a octet stream of a non-null full * pointer in two parts: the first part is a non-zero unsigned long integer * that identifies the referent; the second part is the representation of * the referent. NDR represents subsequent instances in the same octet * stream of the same pointer only by the referent identifier. */ struct ndr_ptr { __u32 ptr; }; struct ndr_uniq_ptr { __u32 ref_id; __u32 ptr; }; struct ndr_char_ptr { char *ptr; }; struct ndr_uniq_char_ptr { __u32 ref_id; char *ptr; }; #define STR_VAL(x) ((x).ptr) struct srvsvc_share_info_request { int level; size_t max_size; struct ndr_uniq_char_ptr server_name; struct ndr_char_ptr share_name; struct ndr_uniq_ptr payload_handle; }; struct wkssvc_netwksta_info_request { struct ndr_uniq_char_ptr server_name; int level; }; struct samr_info_request { int level; int client_version; struct ndr_uniq_char_ptr name; unsigned char handle[20]; unsigned int rid; }; struct lsarpc_info_request { unsigned char handle[20]; unsigned int level; }; struct dcerpc_guid { __u32 time_low; __u16 time_mid; __u16 time_hi_and_version; __u8 clock_seq[2]; __u8 node[6]; }; struct dcerpc_syntax { struct dcerpc_guid uuid; __u16 ver_major; __u16 ver_minor; }; struct dcerpc_context { __u16 id; __u8 num_syntaxes; struct dcerpc_syntax abstract_syntax; struct dcerpc_syntax *transfer_syntaxes; }; struct dcerpc_bind_request { __u32 flags; __u16 max_xmit_frag_sz; __u16 max_recv_frag_sz; __u32 assoc_group_id; __u8 num_contexts; struct dcerpc_context *list; }; enum DCERPC_BIND_ACK_RESULT { DCERPC_BIND_ACK_RES_ACCEPT = 0, DCERPC_BIND_ACK_RES_USER_REJECT, DCERPC_BIND_ACK_RES_PROVIDER_REJECT, DCERPC_BIND_ACK_RES_NEGOTIATE_ACK }; enum DCERPC_BIND_ACK_REASON { DCERPC_BIND_ACK_RSN_NOT_SPECIFIED = 0, DCERPC_BIND_ACK_RSN_ABSTRACT_SYNTAX_NOT_SUPPORTED, DCERPC_BIND_ACK_RSN_TRANSFER_SYNTAXES_NOT_SUPPORTED, DCERPC_BIND_ACK_RSN_LOCAL_LIMIT_EXCEEDED }; enum DCERPC_BIND_NAK_REASON { DCERPC_BIND_NAK_RSN_NOT_SPECIFIED = 0, DCERPC_BIND_NAK_RSN_TEMPORARY_CONGESTION = 1, DCERPC_BIND_NAK_RSN_LOCAL_LIMIT_EXCEEDED = 2, DCERPC_BIND_NAK_RSN_PROTOCOL_VERSION_NOT_SUPPORTED = 4, DCERPC_BIND_NAK_RSN_INVALID_AUTH_TYPE = 8, DCERPC_BIND_NAK_RSN_INVALID_CHECKSUM = 9 }; enum DCERPC_BIND_TIME_OPTIONS { DCERPC_BIND_TIME_OPT_SEC_CONTEXT_MULTIPLEXING = 0x0001, DCERPC_BIND_TIME_OPT_KEEP_CONNECTION_ON_ORPHAN = 0x0002, }; /* * So how this is expected to work. First, you need to obtain a snapshot * of the data that you want to push to the wire. The data snapshot goes * to ksmbd_rpc_pipe. Then you perform a protocol specific transformation * of the data snapshot. The transformed data goes to a specific protocol * dependent structure, e.g. ksmbd_dcerpc for DCERPC (ndr/ndr64). Then you * write the transformed data snapshot to the wire. */ struct ksmbd_rpc_command; struct ksmbd_dcerpc { unsigned int flags; size_t offset; size_t payload_sz; char *payload; int num_pointers; union { struct dcerpc_header hdr; }; union { struct dcerpc_request_header req_hdr; struct dcerpc_response_header resp_hdr; }; union { struct srvsvc_share_info_request si_req; struct dcerpc_bind_request bi_req; struct wkssvc_netwksta_info_request wi_req; struct samr_info_request sm_req; struct lsarpc_info_request lr_req; }; struct ksmbd_rpc_command *rpc_req; struct ksmbd_rpc_command *rpc_resp; /* * Find out the estimated entry size under the given container level * restriction */ int (*entry_size)(struct ksmbd_dcerpc *dce, gpointer entry); /* * Entry representation under the given container level * restriction for array representation */ int (*entry_rep)(struct ksmbd_dcerpc *dce, gpointer entry); /* * Entry data under the given container level restriction * for array representation */ int (*entry_data)(struct ksmbd_dcerpc *dce, gpointer entry); }; struct ksmbd_rpc_pipe { unsigned int id; int num_entries; int num_processed; GArray *entries; struct ksmbd_dcerpc *dce; /* * Tell pipe that we processed the entry and won't need it * anymore so it can remove/drop it. */ int (*entry_processed)(struct ksmbd_rpc_pipe *pipe, int i); }; __u8 ndr_read_int8(struct ksmbd_dcerpc *dce); __u16 ndr_read_int16(struct ksmbd_dcerpc *dce); __u32 ndr_read_int32(struct ksmbd_dcerpc *dce); __u64 ndr_read_int64(struct ksmbd_dcerpc *dce); int ndr_write_int8(struct ksmbd_dcerpc *dce, __u8 value); int ndr_write_int16(struct ksmbd_dcerpc *dce, __u16 value); int ndr_write_int32(struct ksmbd_dcerpc *dce, __u32 value); int ndr_write_int64(struct ksmbd_dcerpc *dce, __u64 value); int ndr_write_union_int16(struct ksmbd_dcerpc *dce, __u16 value); int ndr_write_union_int32(struct ksmbd_dcerpc *dce, __u32 value); __u32 ndr_read_union_int32(struct ksmbd_dcerpc *dce); int ndr_write_bytes(struct ksmbd_dcerpc *dce, void *value, size_t sz); int ndr_read_bytes(struct ksmbd_dcerpc *dce, void *value, size_t sz); int ndr_write_vstring(struct ksmbd_dcerpc *dce, void *value); int ndr_write_string(struct ksmbd_dcerpc *dce, char *str); int ndr_write_lsa_string(struct ksmbd_dcerpc *dce, char *str); char *ndr_read_vstring(struct ksmbd_dcerpc *dce); void ndr_read_vstring_ptr(struct ksmbd_dcerpc *dce, struct ndr_char_ptr *ctr); void ndr_read_uniq_vsting_ptr(struct ksmbd_dcerpc *dce, struct ndr_uniq_char_ptr *ctr); void ndr_free_vstring_ptr(struct ndr_char_ptr *ctr); void ndr_free_uniq_vsting_ptr(struct ndr_uniq_char_ptr *ctr); void ndr_read_ptr(struct ksmbd_dcerpc *dce, struct ndr_ptr *ctr); void ndr_read_uniq_ptr(struct ksmbd_dcerpc *dce, struct ndr_uniq_ptr *ctr); int __ndr_write_array_of_structs(struct ksmbd_rpc_pipe *pipe, int max_entry_nr); int ndr_write_array_of_structs(struct ksmbd_rpc_pipe *pipe); int dcerpc_write_headers(struct ksmbd_dcerpc *dce, int method_status); void dcerpc_set_ext_payload(struct ksmbd_dcerpc *dce, void *payload, size_t sz); void rpc_pipe_reset(struct ksmbd_rpc_pipe *pipe); int rpc_init(void); void rpc_destroy(void); int rpc_restricted_context(struct ksmbd_rpc_command *req); int rpc_ioctl_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp, int max_resp_sz); int rpc_read_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp, int max_resp_sz); int rpc_write_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp); int rpc_open_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp); int rpc_close_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp); void auto_align_offset(struct ksmbd_dcerpc *dce); #endif /* __KSMBD_RPC_H__ */ ksmbd-tools-3.4.4/include/rpc_lsarpc.h000066400000000000000000000014661417226526000177200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2020 Samsung Electronics Co., Ltd. * * Author(s): Namjae Jeon (linkinjeon@kernel.org) */ #ifndef __KSMBD_RPC_LSARPC_H__ #define __KSMBD_RPC_LSARPC_H__ #include #define HANDLE_SIZE 20 struct ksmbd_rpc_command; struct ksmbd_rpc_pipe; struct policy_handle { unsigned char handle[HANDLE_SIZE]; struct ksmbd_user *user; }; struct lsarpc_names_info { unsigned int index; int type; char domain_str[NAME_MAX]; struct smb_sid sid; struct ksmbd_user *user; }; int rpc_lsarpc_read_request(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz); int rpc_lsarpc_write_request(struct ksmbd_rpc_pipe *pipe); int rpc_lsarpc_init(void); void rpc_lsarpc_destroy(void); #endif /* __KSMBD_RPC_LSARPC_H__ */ ksmbd-tools-3.4.4/include/rpc_samr.h000066400000000000000000000012671417226526000173750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2020 Samsung Electronics Co., Ltd. * * Author(s): Namjae Jeon (linkinjeon@kernel.org) */ #ifndef __KSMBD_RPC_SAMR_H__ #define __KSMBD_RPC_SAMR_H__ #include #define HANDLE_SIZE 20 struct ksmbd_rpc_command; struct ksmbd_rpc_pipe; struct connect_handle { unsigned char handle[HANDLE_SIZE]; unsigned int refcount; struct ksmbd_user *user; }; int rpc_samr_read_request(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz); int rpc_samr_write_request(struct ksmbd_rpc_pipe *pipe); int rpc_samr_init(void); void rpc_samr_destroy(void); #endif /* __KSMBD_RPC_SAMR_H__ */ ksmbd-tools-3.4.4/include/rpc_srvsvc.h000066400000000000000000000007511417226526000177560ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __KSMBD_RPC_SRVSVC_H__ #define __KSMBD_RPC_SRVSVC_H__ struct ksmbd_rpc_command; struct ksmbd_rpc_pipe; int rpc_srvsvc_read_request(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz); int rpc_srvsvc_write_request(struct ksmbd_rpc_pipe *pipe); #endif /* __KSMBD_RPC_SRVSVC_H__ */ ksmbd-tools-3.4.4/include/rpc_wkssvc.h000066400000000000000000000007511417226526000177500ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __KSMBD_RPC_WKSSVC_H__ #define __KSMBD_RPC_WKSSVC_H__ struct ksmbd_rpc_command; struct ksmbd_rpc_pipe; int rpc_wkssvc_read_request(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz); int rpc_wkssvc_write_request(struct ksmbd_rpc_pipe *pipe); #endif /* __KSMBD_RPC_WKSSVC_H__ */ ksmbd-tools-3.4.4/include/smbacl.h000066400000000000000000000043441417226526000170270ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ /* * Copyright (c) International Business Machines Corp., 2007 * Author(s): Steve French (sfrench@us.ibm.com) * Copyright (C) 2020 Samsung Electronics Co., Ltd. * Author(s): Namjae Jeon (linkinjeon@kernel.org) */ #ifndef __KSMBD_SMBACL_H__ #define __KSMBD_SMBACL_H__ #include #include #include #define NUM_AUTHS (6) /* number of authority fields */ #define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ #define ACCESS_ALLOWED 0 #define ACCESS_DENIED 1 /* Control flags for Security Descriptor */ #define OWNER_DEFAULTED 0x0001 #define GROUP_DEFAULTED 0x0002 #define DACL_PRESENT 0x0004 #define DACL_DEFAULTED 0x0008 #define SACL_PRESENT 0x0010 #define SACL_DEFAULTED 0x0020 #define DACL_TRUSTED 0x0040 #define SERVER_SECURITY 0x0080 #define DACL_AUTO_INHERIT_REQ 0x0100 #define SACL_AUTO_INHERIT_REQ 0x0200 #define DACL_AUTO_INHERITED 0x0400 #define SACL_AUTO_INHERITED 0x0800 #define DACL_PROTECTED 0x1000 #define SACL_PROTECTED 0x2000 #define RM_CONTROL_VALID 0x4000 #define SELF_RELATIVE 0x8000 #define SID_TYPE_USER 1 #define SID_TYPE_GROUP 2 #define SID_TYPE_UNKNOWN 8 struct smb_ntsd { __u16 revision; /* revision level */ __u16 type; __u32 osidoffset; __u32 gsidoffset; __u32 sacloffset; __u32 dacloffset; }; struct smb_sid { __u8 revision; /* revision level */ __u8 num_subauth; __u8 authority[NUM_AUTHS]; __u32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ }; struct smb_acl { __u16 revision; /* revision level */ __u16 size; __u32 num_aces; }; struct smb_ace { __u8 type; __u8 flags; __u16 size; __u32 access_req; struct smb_sid sid; /* ie UUID of user or group who gets these perms */ }; void smb_init_domain_sid(struct smb_sid *sid); void smb_read_sid(struct ksmbd_dcerpc *dce, struct smb_sid *sid); void smb_write_sid(struct ksmbd_dcerpc *dce, const struct smb_sid *src); void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src); int smb_compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); int build_sec_desc(struct ksmbd_dcerpc *dce, __u32 *secdesclen, int rid); int set_domain_name(struct smb_sid *sid, char *domain, int *type); #endif /* __KSMBD_SMBACL_H__ */ ksmbd-tools-3.4.4/include/version.h000066400000000000000000000003051417226526000172440ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2020 Namjae Jeon */ #ifndef _VERSION_H #define KSMBD_TOOLS_VERSION "3.4.4" #endif /* !_VERSION_H */ ksmbd-tools-3.4.4/include/worker.h000066400000000000000000000005511417226526000170730ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __KSMBD_WORKER__H__ #define __KSMBD_WORKER__H__ struct ksmbd_ipc_msg; int wp_ipc_msg_push(struct ksmbd_ipc_msg *msg); void wp_destroy(void); int wp_init(void); #endif /* __KSMBD_WORKER_H__ */ ksmbd-tools-3.4.4/ksmbd-tools.spec000066400000000000000000000033361417226526000171040ustar00rootroot00000000000000# # spec file for package ksmbd-tools # # Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # # Please submit bugfixes or comments via https://bugs.opensuse.org/ # Name: ksmbd-tools Version: 3.4.2 Release: 0 Summary: cifsd/ksmbd kernel server userspace utilities License: GPL-2.0-or-later Group: System/Filesystems Url: https://github.com/cifsd-team/ksmbd-tools Source: %{name}-%{version}.tar.bz2 # ksmbd kernel module was only added in kernel 5.15 BuildRequires: kernel-default >= 5.15 BuildRequires: glib2-devel BuildRequires: libnl3-devel BuildRequires: autoconf BuildRequires: automake BuildRequires: libtool Requires(pre): kernel-default >= 5.15 %description Set of utilities for creating and managing SMB3 shares for the ksmbd kernel module. %prep %setup -q %build ./autogen.sh %configure make %{?_smp_mflags} %install mkdir -p %{buildroot}/%{_sysconfdir}/ksmbd %make_install install -m 644 -p smb.conf.example %{buildroot}%{_sysconfdir}/ksmbd %files %{_sbindir}/ksmbd.addshare %{_sbindir}/ksmbd.adduser %{_sbindir}/ksmbd.control %{_sbindir}/ksmbd.mountd %dir %{_sysconfdir}/ksmbd %config %{_sysconfdir}/ksmbd/smb.conf.example %changelog ksmbd-tools-3.4.4/ksmbd.service000066400000000000000000000006031417226526000164460ustar00rootroot00000000000000[Unit] Description=ksmbd userspace daemon Wants=network-online.target After=network.target network-online.target [Service] Type=oneshot User=root Group=root RemainAfterExit=yes ExecStartPre=-/sbin/modprobe ksmbd ExecStart=/sbin/ksmbd.mountd -s ExecReload=/bin/sh -c '/sbin/ksmbd.control -s && /sbin/ksmbd.mountd -s' ExecStop=/sbin/ksmbd.control -s [Install] WantedBy=multi-user.target ksmbd-tools-3.4.4/lib/000077500000000000000000000000001417226526000145335ustar00rootroot00000000000000ksmbd-tools-3.4.4/lib/Makefile.am000066400000000000000000000006761417226526000166000ustar00rootroot00000000000000AM_CFLAGS = -I$(top_srcdir)/include $(GLIB_CFLAGS) -fno-common LIBS = $(GLIB_LIBS) noinst_LIBRARIES = libksmbdtools.a libksmbdtools_a_SOURCES = management/tree_conn.c \ management/user.c \ management/share.c \ management/session.c \ config_parser.c \ ksmbdtools.c if HAVE_LIBKRB5 libksmbdtools_a_SOURCES += management/spnego.c \ asn1.c \ management/spnego_krb5.c \ management/spnego_mech.h endif ksmbd-tools-3.4.4/lib/asn1.c000066400000000000000000000164721417226526000155530ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich * * Copyright (c) 2000 RP Internet (www.rpi.net.au). */ /***************************************************************************** * * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse) * *****************************************************************************/ #include #include #include #include #include "asn1.h" void asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len) { ctx->begin = buf; ctx->end = buf + len; ctx->pointer = buf; ctx->error = ASN1_ERR_NOERROR; } static unsigned char asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch) { if (ctx->pointer >= ctx->end) { ctx->error = ASN1_ERR_DEC_EMPTY; return 0; } *ch = *(ctx->pointer)++; return 1; } static unsigned char asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag) { unsigned char ch; *tag = 0; do { if (!asn1_octet_decode(ctx, &ch)) return 0; *tag <<= 7; *tag |= ch & 0x7F; } while ((ch & 0x80) == 0x80); return 1; } static unsigned char asn1_id_decode(struct asn1_ctx *ctx, unsigned int *cls, unsigned int *con, unsigned int *tag) { unsigned char ch; if (!asn1_octet_decode(ctx, &ch)) return 0; *cls = (ch & 0xC0) >> 6; *con = (ch & 0x20) >> 5; *tag = (ch & 0x1F); if (*tag == 0x1F) { if (!asn1_tag_decode(ctx, tag)) return 0; } return 1; } static unsigned char asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len) { unsigned char ch, cnt; if (!asn1_octet_decode(ctx, &ch)) return 0; if (ch == 0x80) *def = 0; else { *def = 1; if (ch < 0x80) *len = ch; else { cnt = (unsigned char) (ch & 0x7F); *len = 0; while (cnt > 0) { if (!asn1_octet_decode(ctx, &ch)) return 0; *len <<= 8; *len |= ch; cnt--; } } } /* don't trust len bigger than ctx buffer */ if (*len > ctx->end - ctx->pointer) return 0; return 1; } unsigned char asn1_header_decode(struct asn1_ctx *ctx, unsigned char **eoc, unsigned int *cls, unsigned int *con, unsigned int *tag) { unsigned int def = 0; unsigned int len = 0; if (!asn1_id_decode(ctx, cls, con, tag)) return 0; if (!asn1_length_decode(ctx, &def, &len)) return 0; /* primitive shall be definite, indefinite shall be constructed */ if (*con == ASN1_PRI && !def) return 0; if (def) *eoc = ctx->pointer + len; else *eoc = NULL; return 1; } static unsigned char asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc) { unsigned char ch; if (eoc == NULL) { if (!asn1_octet_decode(ctx, &ch)) return 0; if (ch != 0x00) { ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; return 0; } if (!asn1_octet_decode(ctx, &ch)) return 0; if (ch != 0x00) { ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; return 0; } return 1; } if (ctx->pointer != eoc) { ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH; return 0; } return 1; } unsigned char asn1_octets_decode(struct asn1_ctx *ctx, unsigned char *eoc, unsigned char **octets, unsigned int *len) { unsigned char *ptr; *len = 0; *octets = malloc(eoc - ctx->pointer); if (*octets == NULL) return 0; ptr = *octets; while (ctx->pointer < eoc) { if (!asn1_octet_decode(ctx, (unsigned char *) ptr++)) { free(*octets); *octets = NULL; return 0; } (*len)++; } return 1; } unsigned char asn1_read(struct asn1_ctx *ctx, unsigned char **buf, unsigned int len) { *buf = NULL; if (ctx->end - ctx->pointer < len) { ctx->error = ASN1_ERR_DEC_EMPTY; return 0; } *buf = malloc(len); memcpy(*buf, ctx->pointer, len); ctx->pointer += len; return 1; } static unsigned char asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid) { unsigned char ch; *subid = 0; do { if (!asn1_octet_decode(ctx, &ch)) return 0; *subid <<= 7; *subid |= ch & 0x7F; } while ((ch & 0x80) == 0x80); return 1; } int asn1_oid_decode(struct asn1_ctx *ctx, unsigned char *eoc, unsigned long **oid, unsigned int *len) { unsigned long subid; unsigned int size; unsigned long *optr; size = eoc - ctx->pointer + 1; /* first subid actually encodes first two subids */ if (size < 2 || size > UINT_MAX/sizeof(unsigned long)) return 0; *oid = calloc(size, sizeof(unsigned long)); if (*oid == NULL) return 0; optr = *oid; if (!asn1_subid_decode(ctx, &subid)) { free(*oid); *oid = NULL; return 0; } if (subid < 40) { optr[0] = 0; optr[1] = subid; } else if (subid < 80) { optr[0] = 1; optr[1] = subid - 40; } else { optr[0] = 2; optr[1] = subid - 80; } *len = 2; optr += 2; while (ctx->pointer < eoc) { if (++(*len) > size) { ctx->error = ASN1_ERR_DEC_BADVALUE; free(*oid); *oid = NULL; return 0; } if (!asn1_subid_decode(ctx, optr++)) { free(*oid); *oid = NULL; return 0; } } return 1; } /* return the size of @depth-nested headers + payload */ int asn1_header_len(unsigned int payload_len, int depth) { unsigned int len; int i; len = payload_len; for (i = 0; i < depth; i++) { /* length */ if (len >= (1 << 24)) len += 5; else if (len >= (1 << 16)) len += 4; else if (len >= (1 << 8)) len += 3; else if (len >= (1 << 7)) len += 2; else len += 1; /* 1-byte header */ len += 1; } return len; } int asn1_oid_encode(const unsigned long *in_oid, int in_len, unsigned char **out_oid, int *out_len) { unsigned char *oid; unsigned long id; int i; *out_oid = calloc(1, in_len * 5); if (*out_oid == NULL) return -ENOMEM; oid = *out_oid; *oid++ = (unsigned char)(40 * in_oid[0] + in_oid[1]); for (i = 2; i < in_len; i++) { id = in_oid[i]; if (id >= (1 << 28)) *oid++ = (0x80 | ((id>>28) & 0x7F)); if (id >= (1 << 21)) *oid++ = (0x80 | ((id>>21) & 0x7F)); if (id >= (1 << 14)) *oid++ = (0x80 | ((id>>14) & 0x7F)); if (id >= (1 << 7)) *oid++ = (0x80 | ((id>>7) & 0x7F)); *oid++ = id & 0x7F; } *out_len = (int)(oid - *out_oid); return 0; } /* * @len is the sum of all sizes of header, length and payload. * it will be decreased by the sum of sizes of header and length. */ int asn1_header_encode(unsigned char **buf, unsigned int cls, unsigned int con, unsigned int tag, unsigned int *len) { unsigned char *loc; unsigned int r_len; /* at least, 1-byte header + 1-byte length is needed. */ if (*len < 2) return -EINVAL; loc = *buf; r_len = *len; *loc++ = ((cls & 0x3) << 6) | ((con & 0x1) << 5) | (tag & 0x1F); r_len -= 1; if (r_len - 1 < (1 << 7)) { r_len -= 1; *loc++ = (unsigned char)(r_len & 0x7F); } else if (r_len - 2 < (1 << 8)) { r_len -= 2; *loc++ = 0x81; *loc++ = (unsigned char)(r_len & 0xFF); } else if (r_len - 3 < (1 << 16)) { r_len -= 3; *loc++ = 0x82; *loc++ = (unsigned char)((r_len>>8) & 0xFF); *loc++ = (unsigned char)(r_len & 0xFF); } else if (r_len - 4 < (1 << 24)) { r_len -= 4; *loc++ = 0x83; *loc++ = (unsigned char)((r_len>>16) & 0xFF); *loc++ = (unsigned char)((r_len>>8) & 0xFF); *loc++ = (unsigned char)(r_len & 0xFF); } else { r_len -= 5; *loc++ = 0x84; *loc++ = (unsigned char)((r_len>>24) & 0xFF); *loc++ = (unsigned char)((r_len>>16) & 0xFF); *loc++ = (unsigned char)((r_len>>8) & 0xFF); *loc++ = (unsigned char)(r_len & 0xFF); } *buf = loc; *len = r_len; return 0; } ksmbd-tools-3.4.4/lib/config_parser.c000066400000000000000000000351231417226526000175240ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #include #include #include struct smbconf_global global_conf; struct smbconf_parser parser; unsigned long long memparse(const char *v) { char *eptr; unsigned long long ret = strtoull(v, &eptr, 0); switch (*eptr) { case 'E': case 'e': ret <<= 10; case 'P': case 'p': ret <<= 10; case 'T': case 't': ret <<= 10; case 'G': case 'g': ret <<= 10; case 'M': case 'm': ret <<= 10; case 'K': case 'k': ret <<= 10; } return ret; } static void kv_release_cb(gpointer p) { g_free(p); } static int is_ascii_space_tab(char c) { return c == ' ' || c == '\t'; } static int is_a_comment(char *line) { return (*line == 0x00 || *line == ';' || *line == '\n' || *line == '#'); } static int is_a_group(char *line) { char *p = line; if (*p != '[') return 0; p++; while (*p && *p != ']') p = g_utf8_find_next_char(p, NULL); if (*p != ']') return 0; return 1; } static int add_new_group(char *line) { char *begin = line; char *end = line; char *name = NULL; struct smbconf_group *group = NULL; struct smbconf_group *lookup; while (*end && *end != ']') end = g_utf8_find_next_char(end, NULL); name = g_ascii_strdown(begin + 1, end - begin - 1); if (!name) goto out_free; lookup = g_hash_table_lookup(parser.groups, name); if (lookup) { parser.current = lookup; pr_info("SMB conf: multiple group definitions `%s'\n", name); g_free(name); return 0; } group = g_malloc(sizeof(struct smbconf_group)); if (!group) goto out_free; group->name = name; group->kv = g_hash_table_new_full(g_str_hash, g_str_equal, kv_release_cb, kv_release_cb); if (!group->kv) goto out_free; parser.current = group; g_hash_table_insert(parser.groups, group->name, group); return 0; out_free: g_free(name); if (group && group->kv) g_hash_table_destroy(group->kv); g_free(group); return -ENOMEM; } static int add_group_key_value(char *line) { char *key, *value; key = strchr(line, '='); if (!key) return -EINVAL; value = key; *key = 0x00; key--; value++; while (is_ascii_space_tab(*key)) key--; while (is_ascii_space_tab(*value)) value++; if (is_a_comment(value)) return 0; if (g_hash_table_lookup(parser.current->kv, key)) { pr_info("SMB conf: multuple key-value [%s] %s\n", parser.current->name, key); return 0; } key = g_strndup(line, key - line + 1); value = g_strdup(value); if (!key || !value) { g_free(key); g_free(value); return -ENOMEM; } g_hash_table_insert(parser.current->kv, key, value); return 0; } static int process_smbconf_entry(char *data) { while (is_ascii_space_tab(*data)) data++; if (is_a_comment(data)) return 0; if (is_a_group(data)) return add_new_group(data); return add_group_key_value(data); } static int __mmap_parse_file(const char *fname, int (*callback)(char *data)) { GMappedFile *file; GError *err = NULL; gchar *contents; int len; char *delim; int fd, ret = 0; fd = g_open(fname, O_RDONLY, 0); if (fd == -1) { ret = errno; pr_err("Can't open `%s': %s\n", fname, strerr(ret)); return -ret; } file = g_mapped_file_new_from_fd(fd, FALSE, &err); if (err) { pr_err("%s: `%s'\n", err->message, fname); g_error_free(err); ret = -EINVAL; goto out; } contents = g_mapped_file_get_contents(file); if (!contents) goto out; len = g_mapped_file_get_length(file); while (len > 0) { delim = strchr(contents, '\n'); if (!delim) delim = contents + len - 1; if (delim) { size_t sz = delim - contents; char *data; if (delim == contents) { contents = delim + 1; len--; continue; } if (!sz) break; data = g_malloc(sz + 1); if (!data) { ret = -ENOMEM; goto out; } strncpy(data, contents, sz); data[sz] = 0x00; ret = callback(data); if (ret) { g_free(data); goto out; } g_free(data); contents = delim + 1; len -= (sz + 1); } } ret = 0; out: if (file) g_mapped_file_unref(file); if (fd) { g_close(fd, &err); if (err) { pr_err("%s: %s\n", err->message, fname); g_error_free(err); } } return ret; } static int init_smbconf_parser(void) { if (parser.groups) return 0; parser.groups = g_hash_table_new(g_str_hash, g_str_equal); if (!parser.groups) return -ENOMEM; return 0; } static void release_smbconf_group(gpointer k, gpointer v, gpointer user_data) { struct smbconf_group *g = v; g_hash_table_destroy(g->kv); g_free(g->name); g_free(g); } static void release_smbconf_parser(void) { if (!parser.groups) return; g_hash_table_foreach(parser.groups, release_smbconf_group, NULL); g_hash_table_destroy(parser.groups); parser.groups = NULL; } char *cp_ltrim(char *v) { if (!v) return NULL; while (*v && *v == ' ') v++; if (*v == 0x00) return NULL; return v; } int cp_key_cmp(char *k, char *v) { if (!k || !v) return -1; return g_ascii_strncasecmp(k, v, strlen(v)); } char *cp_get_group_kv_string(char *v) { return g_strdup(v); } int cp_get_group_kv_bool(char *v) { if (!g_ascii_strncasecmp(v, "yes", 3) || !g_ascii_strncasecmp(v, "1", 1) || !g_ascii_strncasecmp(v, "true", 4) || !g_ascii_strncasecmp(v, "enable", 6)) return 1; return 0; } int cp_get_group_kv_config_opt(char *v) { if (!g_ascii_strncasecmp(v, "disabled", 8)) return KSMBD_CONFIG_OPT_DISABLED; if (!g_ascii_strncasecmp(v, "enabled", 7)) return KSMBD_CONFIG_OPT_ENABLED; if (!g_ascii_strncasecmp(v, "auto", 4)) return KSMBD_CONFIG_OPT_AUTO; if (!g_ascii_strncasecmp(v, "mandatory", 9)) return KSMBD_CONFIG_OPT_MANDATORY; return KSMBD_CONFIG_OPT_DISABLED; } unsigned long cp_get_group_kv_long_base(char *v, int base) { return strtoul(v, NULL, base); } unsigned long cp_get_group_kv_long(char *v) { return cp_get_group_kv_long_base(v, 10); } char **cp_get_group_kv_list(char *v) { /* * SMB conf lists are "tabs, spaces, commas" separated. */ return g_strsplit_set(v, "\t ,", -1); } void cp_group_kv_list_free(char **list) { g_strfreev(list); } static int cp_add_global_guest_account(gpointer _v) { struct ksmbd_user *user; if (usm_add_new_user(cp_get_group_kv_string(_v), g_strdup("NULL"))) { pr_err("Unable to add guest account\n"); return -ENOMEM; } user = usm_lookup_user(_v); if (!user) { pr_err("Fatal error: unable to find `%s' account.\n", (const char *) _v); return -EINVAL; } set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT); put_ksmbd_user(user); global_conf.guest_account = cp_get_group_kv_string(_v); return 0; } static void global_group_kv(gpointer _k, gpointer _v, gpointer user_data) { if (!cp_key_cmp(_k, "server string")) { global_conf.server_string = cp_get_group_kv_string(_v); return; } if (!cp_key_cmp(_k, "workgroup")) { global_conf.work_group = cp_get_group_kv_string(_v); return; } if (!cp_key_cmp(_k, "netbios name")) { global_conf.netbios_name = cp_get_group_kv_string(_v); return; } if (!cp_key_cmp(_k, "server min protocol")) { global_conf.server_min_protocol = cp_get_group_kv_string(_v); return; } if (!cp_key_cmp(_k, "server signing")) { global_conf.server_signing = cp_get_group_kv_config_opt(_v); return; } if (!cp_key_cmp(_k, "server max protocol")) { global_conf.server_max_protocol = cp_get_group_kv_string(_v); return; } if (!cp_key_cmp(_k, "guest account")) { cp_add_global_guest_account(_v); return; } if (!cp_key_cmp(_k, "max active sessions")) { global_conf.sessions_cap = cp_get_group_kv_long(_v); return; } if (!cp_key_cmp(_k, "tcp port")) { if (!global_conf.tcp_port) global_conf.tcp_port = cp_get_group_kv_long(_v); return; } if (!cp_key_cmp(_k, "ipc timeout")) { global_conf.ipc_timeout = cp_get_group_kv_long(_v); return; } if (!cp_key_cmp(_k, "max open files")) { global_conf.file_max = cp_get_group_kv_long(_v); return; } if (!cp_key_cmp(_k, "restrict anonymous")) { global_conf.restrict_anon = cp_get_group_kv_long(_v); if (global_conf.restrict_anon > KSMBD_RESTRICT_ANON_TYPE_2 || global_conf.restrict_anon < 0) { global_conf.restrict_anon = 0; pr_err("Invalid restrict anonymous value\n"); } return; } if (!cp_key_cmp(_k, "map to guest")) { global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_NEVER; if (!cp_key_cmp(_v, "bad user")) global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_BAD_USER; if (!cp_key_cmp(_v, "bad password")) global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD; if (!cp_key_cmp(_v, "bad uid")) global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_BAD_UID; return; } if (!cp_key_cmp(_k, "bind interfaces only")) { global_conf.bind_interfaces_only = cp_get_group_kv_bool(_v); return; } if (!cp_key_cmp(_k, "interfaces")) { global_conf.interfaces = cp_get_group_kv_list(_v); return; } if (!cp_key_cmp(_k, "deadtime")) { global_conf.deadtime = cp_get_group_kv_long(_v); return; } if (!cp_key_cmp(_k, "smb2 leases")) { if (cp_get_group_kv_bool(_v)) global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB2_LEASES; else global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB2_LEASES; return; } if (!cp_key_cmp(_k, "root directory")) { global_conf.root_dir = cp_get_group_kv_string(_v); return; } if (!cp_key_cmp(_k, "smb2 max read")) { global_conf.smb2_max_read = memparse(_v); return; } if (!cp_key_cmp(_k, "smb2 max write")) { global_conf.smb2_max_write = memparse(_v); return; } if (!cp_key_cmp(_k, "smb2 max trans")) { global_conf.smb2_max_trans = memparse(_v); return; } if (!cp_key_cmp(_k, "smb3 encryption")) { if (cp_get_group_kv_bool(_v)) global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION; else global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION; return; } if (!cp_key_cmp(_k, "share:fake_fscaps")) { global_conf.share_fake_fscaps = cp_get_group_kv_long(_v); return; } if (!cp_key_cmp(_k, "kerberos service name")) { global_conf.krb5_service_name = cp_get_group_kv_string(_v); return; } if (!cp_key_cmp(_k, "kerberos keytab file")) { global_conf.krb5_keytab_file = cp_get_group_kv_string(_v); return; } if (!cp_key_cmp(_k, "server multi channel support")) { if (cp_get_group_kv_bool(_v)) global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL; else global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL; return; } if (!cp_key_cmp(_k, "smb2 max credits")) { global_conf.smb2_max_credits = memparse(_v); return; } } static void fixup_missing_global_group(void) { int ret; /* * Set default global parameters which were not specified * in smb.conf */ if (!global_conf.file_max) global_conf.file_max = KSMBD_CONF_FILE_MAX; if (!global_conf.server_string) global_conf.server_string = cp_get_group_kv_string( KSMBD_CONF_DEFAULT_SERVER_STRING); if (!global_conf.netbios_name) global_conf.netbios_name = cp_get_group_kv_string(KSMBD_CONF_DEFAULT_NETBIOS_NAME); if (!global_conf.work_group) global_conf.work_group = cp_get_group_kv_string(KSMBD_CONF_DEFAULT_WORK_GROUP); if (!global_conf.tcp_port) global_conf.tcp_port = KSMBD_CONF_DEFAULT_TPC_PORT; if (global_conf.sessions_cap <= 0) global_conf.sessions_cap = KSMBD_CONF_DEFAULT_SESS_CAP; if (global_conf.guest_account) return; ret = cp_add_global_guest_account(KSMBD_CONF_DEFAULT_GUEST_ACCOUNT); if (!ret) return; ret = cp_add_global_guest_account(KSMBD_CONF_FALLBACK_GUEST_ACCOUNT); if (ret) pr_err("Fatal error: Cannot set a global guest account %d\n", ret); } static void default_global_group(void) { /* The SPARSE_FILES file system capability flag is set by default */ global_conf.share_fake_fscaps = 64; } static void global_group(struct smbconf_group *group) { g_hash_table_foreach(group->kv, global_group_kv, NULL); } #define GROUPS_CALLBACK_STARTUP_INIT 0x1 #define GROUPS_CALLBACK_REINIT 0x2 static void groups_callback(gpointer _k, gpointer _v, gpointer flags) { if (g_ascii_strncasecmp(_k, "global", 6)) { shm_add_new_share((struct smbconf_group *)_v); return; } if (flags == (gpointer)GROUPS_CALLBACK_STARTUP_INIT) global_group((struct smbconf_group *)_v); } static int cp_add_ipc_share(void) { char *comment = NULL, *guest = NULL; int ret = 0; if (g_hash_table_lookup(parser.groups, "ipc$")) return 0; comment = g_strdup("comment = IPC share"); guest = g_strdup("guest ok = yes"); ret = add_new_group("[IPC$]"); ret |= add_group_key_value(comment); ret |= add_group_key_value(guest); if (ret) { pr_err("Unable to add IPC$ share\n"); ret = -EINVAL; goto out; } return ret; out: g_free(comment); g_free(guest); return ret; } static int __cp_parse_smbconfig(const char *smbconf, GHFunc cb, long flags) { int ret; default_global_group(); ret = cp_smbconfig_hash_create(smbconf); if (ret) return ret; ret = cp_add_ipc_share(); if (!ret) { g_hash_table_foreach(parser.groups, groups_callback, (gpointer)flags); fixup_missing_global_group(); } cp_smbconfig_destroy(); return ret; } int cp_parse_reload_smbconf(const char *smbconf) { return __cp_parse_smbconfig(smbconf, groups_callback, GROUPS_CALLBACK_REINIT); } int cp_parse_smbconf(const char *smbconf) { return __cp_parse_smbconfig(smbconf, groups_callback, GROUPS_CALLBACK_STARTUP_INIT); } int cp_parse_pwddb(const char *pwddb) { return __mmap_parse_file(pwddb, usm_add_update_user_from_pwdentry); } int cp_smbconfig_hash_create(const char *smbconf) { int ret = init_smbconf_parser(); if (ret) return ret; return __mmap_parse_file(smbconf, process_smbconf_entry); } int cp_parse_subauth(const char *subauth_path) { return __mmap_parse_file(subauth_path, usm_add_subauth_global_conf); } void cp_smbconfig_destroy(void) { release_smbconf_parser(); } int cp_parse_external_smbconf_group(char *name, char *opts) { char *pos; int i, len; if (init_smbconf_parser()) return -EINVAL; if (!opts || !name) return -EINVAL; len = strlen(opts); /* fake smb.conf input */ for (i = 0; i < KSMBD_SHARE_CONF_MAX; i++) { pos = strstr(opts, KSMBD_SHARE_CONF[i]); if (!pos) continue; if (pos != opts) *(pos - 1) = '\n'; } if (add_new_group(name)) goto error; /* split input and feed to normal process_smbconf_entry() */ while (len) { char *delim = strchr(opts, '\n'); if (delim) { *delim = 0x00; len -= delim - opts; } else { len = 0; } process_smbconf_entry(opts); if (delim) opts = delim + 1; } return 0; error: cp_smbconfig_destroy(); return -EINVAL; } ksmbd-tools-3.4.4/lib/ksmbdtools.c000066400000000000000000000115721417226526000170660ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include static const char *app_name = "unknown"; static int log_open; typedef void (*logger)(int level, const char *fmt, va_list list); char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1] = { "UTF-8", "UTF-16LE", "UCS-2LE", "UTF-16BE", "UCS-2BE", "OOPS" }; static int syslog_level(int level) { if (level == PR_ERROR) return LOG_ERR; if (level == PR_INFO) return LOG_INFO; if (level == PR_DEBUG) return LOG_DEBUG; return LOG_ERR; } static void __pr_log_stdio(int level, const char *fmt, va_list list) { char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, list); printf("%s", buf); } static void __pr_log_syslog(int level, const char *fmt, va_list list) { vsyslog(syslog_level(level), fmt, list); } static logger __logger = __pr_log_stdio; void set_logger_app_name(const char *an) { app_name = an; } const char *get_logger_app_name(void) { return app_name; } char *strerr(int err) { static char __thread buf[64]; strerror_r(err, buf, sizeof(buf)); buf[sizeof(buf) - 1] = 0x00; return buf; } void __pr_log(int level, const char *fmt, ...) { va_list list; va_start(list, fmt); __logger(level, fmt, list); va_end(list); } void pr_logger_init(int flag) { if (flag == PR_LOGGER_SYSLOG) { if (log_open) { closelog(); log_open = 0; } openlog("ksmbd", LOG_NDELAY, LOG_LOCAL5); __logger = __pr_log_syslog; log_open = 1; } } #if TRACING_DUMP_NL_MSG #define PR_HEX_DUMP_WIDTH 160 void pr_hex_dump(const void *mem, size_t sz) { char xline[PR_HEX_DUMP_WIDTH]; char sline[PR_HEX_DUMP_WIDTH]; int xi = 0, si = 0, mi = 0; while (mi < sz) { char c = *((char *)mem + mi); mi++; xi += sprintf(xline + xi, "%02X ", 0xff & c); if (c > ' ' && c < '~') si += sprintf(sline + si, "%c", c); else si += sprintf(sline + si, "."); if (xi >= PR_HEX_DUMP_WIDTH / 2) { pr_err("%s %s\n", xline, sline); xi = 0; si = 0; } } if (xi) { int sz = PR_HEX_DUMP_WIDTH / 2 - xi + 1; if (sz > 0) { memset(xline + xi, ' ', sz); xline[PR_HEX_DUMP_WIDTH / 2 + 1] = 0x00; } pr_err("%s %s\n", xline, sline); } } #else void pr_hex_dump(const void *mem, size_t sz) { } #endif char *base64_encode(unsigned char *src, size_t srclen) { return g_base64_encode(src, srclen); } unsigned char *base64_decode(char const *src, size_t *dstlen) { unsigned char *ret = g_base64_decode(src, dstlen); if (ret) ret[*dstlen] = 0x00; return ret; } static int codeset_has_altname(int codeset) { if (codeset == KSMBD_CHARSET_UTF16LE || codeset == KSMBD_CHARSET_UTF16BE) return 1; return 0; } gchar *ksmbd_gconvert(const gchar *str, gssize str_len, int to_codeset, int from_codeset, gsize *bytes_read, gsize *bytes_written) { gchar *converted; GError *err; retry: err = NULL; if (from_codeset >= KSMBD_CHARSET_MAX) { pr_err("Unknown source codeset: %d\n", from_codeset); return NULL; } if (to_codeset >= KSMBD_CHARSET_MAX) { pr_err("Unknown target codeset: %d\n", to_codeset); return NULL; } converted = g_convert(str, str_len, ksmbd_conv_charsets[to_codeset], ksmbd_conv_charsets[from_codeset], bytes_read, bytes_written, &err); if (err) { int has_altname = 0; if (codeset_has_altname(to_codeset)) { to_codeset++; has_altname = 1; } if (codeset_has_altname(from_codeset)) { from_codeset++; has_altname = 1; } pr_info("%s\n", err->message); g_error_free(err); if (has_altname) { pr_info("Will try '%s' and '%s'\n", ksmbd_conv_charsets[to_codeset], ksmbd_conv_charsets[from_codeset]); goto retry; } pr_err("Can't convert string: %s\n", err->message); g_error_free(err); return NULL; } return converted; } static void send_signal_to_ksmbd_daemon(int signo) { char manager_pid[10] = {0, }; int pid = 0; int lock_fd; lock_fd = open(KSMBD_LOCK_FILE, O_RDONLY); if (lock_fd < 0) return; if (read(lock_fd, &manager_pid, sizeof(manager_pid)) == -1) { pr_debug("Unable to read main PID: %s\n", strerr(errno)); close(lock_fd); return; } close(lock_fd); pid = strtol(manager_pid, NULL, 10); pr_debug("Send %d to pid %d\n", signo, pid); if (kill(pid, signo)) pr_debug("Unable to send signal to pid %d: %s\n", pid, strerr(errno)); } void notify_ksmbd_daemon(void) { send_signal_to_ksmbd_daemon(SIGHUP); } void terminate_ksmbd_daemon(void) { send_signal_to_ksmbd_daemon(SIGTERM); } int test_file_access(char *conf) { int fd = open(conf, O_RDWR | O_CREAT, S_IRWXU | S_IRGRP); if (fd != -1) { close(fd); return 0; } pr_err("%s %s\n", conf, strerr(errno)); return -EINVAL; } ksmbd-tools-3.4.4/lib/management/000077500000000000000000000000001417226526000166475ustar00rootroot00000000000000ksmbd-tools-3.4.4/lib/management/session.c000066400000000000000000000121341417226526000204770ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include "linux/ksmbd_server.h" #include "management/session.h" #include "management/tree_conn.h" #include "management/user.h" #include "ksmbdtools.h" static GHashTable *sessions_table; static GRWLock sessions_table_lock; static void __free_func(gpointer data, gpointer user_data) { struct ksmbd_tree_conn *tree_conn; tree_conn = (struct ksmbd_tree_conn *)data; tcm_tree_conn_free(tree_conn); } static void kill_ksmbd_session(struct ksmbd_session *sess) { g_list_foreach(sess->tree_conns, __free_func, NULL); g_list_free(sess->tree_conns); g_rw_lock_clear(&sess->update_lock); free(sess); } static struct ksmbd_session *new_ksmbd_session(unsigned long long id, struct ksmbd_user *user) { struct ksmbd_session *sess; sess = calloc(1, sizeof(struct ksmbd_session)); if (!sess) return NULL; g_rw_lock_init(&sess->update_lock); sess->ref_counter = 1; sess->id = id; sess->user = user; return sess; } static void free_hash_entry(gpointer k, gpointer s, gpointer user_data) { kill_ksmbd_session(s); } static void sm_clear_sessions(void) { g_hash_table_foreach(sessions_table, free_hash_entry, NULL); } static int __sm_remove_session(struct ksmbd_session *sess) { int ret = -EINVAL; g_rw_lock_writer_lock(&sessions_table_lock); if (g_hash_table_remove(sessions_table, &sess->id)) ret = 0; g_rw_lock_writer_unlock(&sessions_table_lock); if (!ret) kill_ksmbd_session(sess); return ret; } static struct ksmbd_session *__get_session(struct ksmbd_session *sess) { struct ksmbd_session *ret = NULL; g_rw_lock_writer_lock(&sess->update_lock); if (sess->ref_counter != 0) { sess->ref_counter++; ret = sess; } else { ret = NULL; } g_rw_lock_writer_unlock(&sess->update_lock); return ret; } static void __put_session(struct ksmbd_session *sess) { int drop = 0; g_rw_lock_writer_lock(&sess->update_lock); sess->ref_counter--; drop = !sess->ref_counter; g_rw_lock_writer_unlock(&sess->update_lock); if (drop) __sm_remove_session(sess); } static struct ksmbd_session *__sm_lookup_session(unsigned long long id) { return g_hash_table_lookup(sessions_table, &id); } static struct ksmbd_session *sm_lookup_session(unsigned long long id) { struct ksmbd_session *sess; g_rw_lock_reader_lock(&sessions_table_lock); sess = __sm_lookup_session(id); if (sess) sess = __get_session(sess); g_rw_lock_reader_unlock(&sessions_table_lock); return sess; } int sm_handle_tree_connect(unsigned long long id, struct ksmbd_user *user, struct ksmbd_tree_conn *tree_conn) { struct ksmbd_session *sess, *lookup; retry: sess = sm_lookup_session(id); if (!sess) { sess = new_ksmbd_session(id, user); if (!sess) return -EINVAL; g_rw_lock_writer_lock(&sessions_table_lock); lookup = __sm_lookup_session(id); if (lookup) lookup = __get_session(lookup); if (lookup) { kill_ksmbd_session(sess); sess = lookup; } if (!g_hash_table_insert(sessions_table, &(sess->id), sess)) { kill_ksmbd_session(sess); sess = NULL; } g_rw_lock_writer_unlock(&sessions_table_lock); if (!sess) goto retry; } g_rw_lock_writer_lock(&sess->update_lock); sess->tree_conns = g_list_insert(sess->tree_conns, tree_conn, -1); g_rw_lock_writer_unlock(&sess->update_lock); return 0; } int sm_check_sessions_capacity(unsigned long long id) { int ret = 0; struct ksmbd_session *sess; sess = sm_lookup_session(id); if (sess) { __put_session(sess); return ret; } if (g_atomic_int_add(&global_conf.sessions_cap, -1) < 1) { ret = -EINVAL; g_atomic_int_inc(&global_conf.sessions_cap); } return ret; } static gint lookup_tree_conn(gconstpointer data, gconstpointer user_data) { struct ksmbd_tree_conn *tree_conn = (struct ksmbd_tree_conn *)data; struct ksmbd_tree_conn *dummy = (struct ksmbd_tree_conn *)user_data; if (tree_conn->id == dummy->id) return 0; return 1; } int sm_handle_tree_disconnect(unsigned long long sess_id, unsigned long long tree_conn_id) { struct ksmbd_tree_conn dummy; struct ksmbd_session *sess; GList *tc_list; sess = sm_lookup_session(sess_id); if (!sess) return 0; g_atomic_int_inc(&global_conf.sessions_cap); g_rw_lock_writer_lock(&sess->update_lock); dummy.id = tree_conn_id; tc_list = g_list_find_custom(sess->tree_conns, &dummy, lookup_tree_conn); if (tc_list) { struct ksmbd_tree_conn *tree_conn; tree_conn = (struct ksmbd_tree_conn *)tc_list->data; sess->tree_conns = g_list_remove(sess->tree_conns, tree_conn); sess->ref_counter--; tcm_tree_conn_free(tree_conn); } g_rw_lock_writer_unlock(&sess->update_lock); put_ksmbd_user(sess->user); __put_session(sess); return 0; } void sm_destroy(void) { if (sessions_table) { sm_clear_sessions(); g_hash_table_destroy(sessions_table); } g_rw_lock_clear(&sessions_table_lock); } int sm_init(void) { sessions_table = g_hash_table_new(g_int64_hash, g_int64_equal); if (!sessions_table) return -ENOMEM; g_rw_lock_init(&sessions_table_lock); return 0; } ksmbd-tools-3.4.4/lib/management/share.c000066400000000000000000000445031417226526000201230ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include "config_parser.h" #include "linux/ksmbd_server.h" #include "management/share.h" #include "management/user.h" #include "ksmbdtools.h" #define KSMBD_SHARE_STATE_FREEING 1 /* * WARNING: * * This must match KSMBD_SHARE_CONF enum 1:1. * Add new entries ONLY to the bottom. */ char *KSMBD_SHARE_CONF[KSMBD_SHARE_CONF_MAX] = { "comment", /* 0 */ "path", "guest ok", "guest account", "read only", "browseable", /* 5 */ "write ok", "writeable", "store dos attributes", "oplocks", "create mask", /* 10 */ "directory mask", "force create mode", "force directory mode", "force group", "force user", /* 15 */ "hide dot files", "valid users", "invalid users", "read list", "write list", /* 20 */ "admin users", "hosts allow", "hosts deny", "max connections", "veto files", /* 25 */ "inherit owner", "follow symlinks", "vfs objects", "writable", }; static GHashTable *shares_table; static GRWLock shares_table_lock; int shm_share_config(char *k, enum KSMBD_SHARE_CONF c) { if (c >= KSMBD_SHARE_CONF_MAX) return 0; return !cp_key_cmp(k, KSMBD_SHARE_CONF[c]); } static void list_hosts_callback(gpointer k, gpointer v, gpointer user_data) { free(k); free(v); } static void free_hosts_map(GHashTable *map) { if (map) { g_hash_table_foreach(map, list_hosts_callback, NULL); g_hash_table_destroy(map); } } static void list_user_callback(gpointer k, gpointer u, gpointer user_data) { put_ksmbd_user((struct ksmbd_user *)u); } static void free_user_map(GHashTable *map) { if (map) { g_hash_table_foreach(map, list_user_callback, NULL); g_hash_table_destroy(map); } } static void kill_ksmbd_share(struct ksmbd_share *share) { int i; pr_debug("Kill share %s\n", share->name); for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++) free_user_map(share->maps[i]); free_hosts_map(share->hosts_allow_map); free_hosts_map(share->hosts_deny_map); g_rw_lock_clear(&share->maps_lock); free(share->name); free(share->path); free(share->comment); free(share->veto_list); free(share->guest_account); g_rw_lock_clear(&share->update_lock); free(share); } static int __shm_remove_share(struct ksmbd_share *share) { int ret = 0; if (share->state != KSMBD_SHARE_STATE_FREEING) { g_rw_lock_writer_lock(&shares_table_lock); if (!g_hash_table_remove(shares_table, share->name)) ret = -EINVAL; g_rw_lock_writer_unlock(&shares_table_lock); } if (!ret) kill_ksmbd_share(share); return ret; } struct ksmbd_share *get_ksmbd_share(struct ksmbd_share *share) { g_rw_lock_writer_lock(&share->update_lock); if (share->ref_count != 0) { share->ref_count++; g_rw_lock_writer_unlock(&share->update_lock); } else { g_rw_lock_writer_unlock(&share->update_lock); share = NULL; } return share; } void put_ksmbd_share(struct ksmbd_share *share) { int drop; if (!share) return; g_rw_lock_writer_lock(&share->update_lock); share->ref_count--; drop = !share->ref_count; g_rw_lock_writer_unlock(&share->update_lock); if (!drop) return; __shm_remove_share(share); } static gboolean put_share_callback(gpointer _k, gpointer _v, gpointer data) { struct ksmbd_share *share = (struct ksmbd_share *)_v; share->state = KSMBD_SHARE_STATE_FREEING; put_ksmbd_share(share); return TRUE; } void shm_remove_all_shares(void) { g_rw_lock_writer_lock(&shares_table_lock); g_hash_table_foreach_remove(shares_table, put_share_callback, NULL); g_rw_lock_writer_unlock(&shares_table_lock); } static struct ksmbd_share *new_ksmbd_share(void) { struct ksmbd_share *share; int i; share = calloc(1, sizeof(struct ksmbd_share)); if (!share) return NULL; share->ref_count = 1; /* * Create maps as needed. NULL maps means that share * does not have a corresponding shmbconf entry. */ for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++) share->maps[i] = NULL; share->hosts_allow_map = NULL; share->hosts_deny_map = NULL; g_rw_lock_init(&share->maps_lock); g_rw_lock_init(&share->update_lock); return share; } static void free_hash_entry(gpointer k, gpointer s, gpointer user_data) { kill_ksmbd_share(s); } static void shm_clear_shares(void) { g_hash_table_foreach(shares_table, free_hash_entry, NULL); } void shm_destroy(void) { if (shares_table) { shm_clear_shares(); g_hash_table_destroy(shares_table); } g_rw_lock_clear(&shares_table_lock); } int shm_init(void) { shares_table = g_hash_table_new(g_str_hash, g_str_equal); if (!shares_table) return -ENOMEM; g_rw_lock_init(&shares_table_lock); return 0; } static struct ksmbd_share *__shm_lookup_share(char *name) { return g_hash_table_lookup(shares_table, name); } struct ksmbd_share *shm_lookup_share(char *name) { struct ksmbd_share *share, *ret; g_rw_lock_reader_lock(&shares_table_lock); share = __shm_lookup_share(name); if (share) { ret = get_ksmbd_share(share); if (!ret) share = NULL; } g_rw_lock_reader_unlock(&shares_table_lock); return share; } static GHashTable *parse_list(GHashTable *map, char **list) { int i; if (!list) return map; if (!map) map = g_hash_table_new(g_str_hash, g_str_equal); if (!map) return map; for (i = 0; list[i] != NULL; i++) { struct ksmbd_user *user; char *p = list[i]; p = cp_ltrim(p); if (!p) continue; user = usm_lookup_user(p); if (!user) { pr_info("Drop non-existing user `%s'\n", p); continue; } if (g_hash_table_lookup(map, user->name)) { pr_debug("User already exists in a map: %s\n", p); continue; } g_hash_table_insert(map, user->name, user); } cp_group_kv_list_free(list); return map; } static void make_veto_list(struct ksmbd_share *share) { int i; for (i = 0; i < share->veto_list_sz; i++) { if (share->veto_list[i] == '/') share->veto_list[i] = 0x00; } } static void force_group(struct ksmbd_share *share, char *name) { struct group *grp; grp = getgrnam(name); if (grp) { share->force_gid = grp->gr_gid; if (share->force_gid == KSMBD_SHARE_INVALID_GID) pr_err("Invalid force gid: %u\n", share->force_gid); } else pr_err("Unable to lookup up /etc/group entry: %s\n", name); } static void force_user(struct ksmbd_share *share, char *name) { struct passwd *passwd; passwd = getpwnam(name); if (passwd) { share->force_uid = passwd->pw_uid; /* * smb.conf 'force group' has higher priority than * 'force user'. */ if (share->force_gid == KSMBD_SHARE_INVALID_GID) share->force_gid = passwd->pw_gid; if (share->force_uid == KSMBD_SHARE_INVALID_UID || share->force_gid == KSMBD_SHARE_INVALID_GID) pr_err("Invalid force uid / gid: %u / %u\n", share->force_uid, share->force_gid); } else { pr_err("Unable to lookup up /etc/passwd entry: %s\n", name); } } static void process_group_kv(gpointer _k, gpointer _v, gpointer user_data) { struct ksmbd_share *share = user_data; char *k = _k; char *v = _v; if (shm_share_config(k, KSMBD_SHARE_CONF_COMMENT)) { share->comment = cp_get_group_kv_string(v); if (share->comment == NULL) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_PATH)) { share->path = cp_get_group_kv_string(v); if (share->path == NULL) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_OK)) { if (cp_get_group_kv_bool(v)) set_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_ACCOUNT)) { struct ksmbd_user *user; if (usm_add_new_user(cp_get_group_kv_string(_v), g_strdup("NULL"))) { pr_err("Unable to add guest account\n"); set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } user = usm_lookup_user(_v); if (user) { set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT); put_ksmbd_user(user); } share->guest_account = cp_get_group_kv_string(_v); if (!share->guest_account) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_READ_ONLY)) { if (cp_get_group_kv_bool(v)) { set_share_flag(share, KSMBD_SHARE_FLAG_READONLY); clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE); } else { clear_share_flag(share, KSMBD_SHARE_FLAG_READONLY); set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE); } return; } if (shm_share_config(k, KSMBD_SHARE_CONF_BROWSEABLE)) { if (cp_get_group_kv_bool(v)) set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE); else clear_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_OK) || shm_share_config(k, KSMBD_SHARE_CONF_WRITEABLE) || shm_share_config(k, KSMBD_SHARE_CONF_WRITABLE)) { if (cp_get_group_kv_bool(v)) set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE); else clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_STORE_DOS_ATTRIBUTES)) { if (cp_get_group_kv_bool(v)) set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS); else clear_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_OPLOCKS)) { if (cp_get_group_kv_bool(v)) set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS); else clear_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_CREATE_MASK)) { share->create_mask = cp_get_group_kv_long_base(v, 8); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_DIRECTORY_MASK)) { share->directory_mask = cp_get_group_kv_long_base(v, 8); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_CREATE_MODE)) { share->force_create_mode = cp_get_group_kv_long_base(v, 8); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_DIRECTORY_MODE)) { share->force_directory_mode = cp_get_group_kv_long_base(v, 8); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_GROUP)) { force_group(share, v); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_USER)) { force_user(share, v); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_HIDE_DOT_FILES)) { if (cp_get_group_kv_bool(v)) set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES); else clear_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES); } if (shm_share_config(k, KSMBD_SHARE_CONF_VALID_USERS)) { share->maps[KSMBD_SHARE_VALID_USERS_MAP] = parse_list(share->maps[KSMBD_SHARE_VALID_USERS_MAP], cp_get_group_kv_list(v)); if (share->maps[KSMBD_SHARE_VALID_USERS_MAP] == NULL) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_INVALID_USERS)) { share->maps[KSMBD_SHARE_INVALID_USERS_MAP] = parse_list(share->maps[KSMBD_SHARE_INVALID_USERS_MAP], cp_get_group_kv_list(v)); if (share->maps[KSMBD_SHARE_INVALID_USERS_MAP] == NULL) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_READ_LIST)) { share->maps[KSMBD_SHARE_READ_LIST_MAP] = parse_list(share->maps[KSMBD_SHARE_READ_LIST_MAP], cp_get_group_kv_list(v)); if (share->maps[KSMBD_SHARE_READ_LIST_MAP] == NULL) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_LIST)) { share->maps[KSMBD_SHARE_WRITE_LIST_MAP] = parse_list(share->maps[KSMBD_SHARE_WRITE_LIST_MAP], cp_get_group_kv_list(v)); if (share->maps[KSMBD_SHARE_WRITE_LIST_MAP] == NULL) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_ADMIN_USERS)) { share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] = parse_list(share->maps[KSMBD_SHARE_ADMIN_USERS_MAP], cp_get_group_kv_list(v)); if (share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] == NULL) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_ALLOW)) { share->hosts_allow_map = parse_list(share->hosts_allow_map, cp_get_group_kv_list(v)); if (share->hosts_allow_map == NULL) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_DENY)) { share->hosts_deny_map = parse_list(share->hosts_deny_map, cp_get_group_kv_list(v)); if (share->hosts_deny_map == NULL) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_MAX_CONNECTIONS)) { share->max_connections = cp_get_group_kv_long_base(v, 10); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_VETO_FILES)) { share->veto_list = cp_get_group_kv_string(v + 1); if (share->veto_list == NULL) { set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); } else { share->veto_list_sz = strlen(share->veto_list); make_veto_list(share); } return; } if (shm_share_config(k, KSMBD_SHARE_CONF_INHERIT_OWNER)) { if (cp_get_group_kv_bool(v)) set_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER); else clear_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER); } if (shm_share_config(k, KSMBD_SHARE_CONF_VFS_OBJECTS)) { char *p; int i; char **objects = cp_get_group_kv_list(v); if (objects) { clear_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR); clear_share_flag(share, KSMBD_SHARE_FLAG_STREAMS); for (i = 0; objects[i] != NULL; i++) { p = cp_ltrim(objects[i]); if (!p) continue; if (!strcmp(p, "acl_xattr")) set_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR); else if (!strcmp(p, "streams_xattr")) set_share_flag(share, KSMBD_SHARE_FLAG_STREAMS); } cp_group_kv_list_free(objects); } } } static void fixup_missing_fields(struct ksmbd_share *share) { if (!share->comment) share->comment = strdup(""); } static void init_share_from_group(struct ksmbd_share *share, struct smbconf_group *group) { share->name = g_strdup(group->name); share->create_mask = KSMBD_SHARE_DEFAULT_CREATE_MASK; share->directory_mask = KSMBD_SHARE_DEFAULT_DIRECTORY_MASK; share->force_create_mode = 0; share->force_directory_mode = 0; share->force_uid = KSMBD_SHARE_INVALID_UID; share->force_gid = KSMBD_SHARE_INVALID_GID; set_share_flag(share, KSMBD_SHARE_FLAG_AVAILABLE); set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE); set_share_flag(share, KSMBD_SHARE_FLAG_READONLY); set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES); set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS); set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS); if (!cp_key_cmp(share->name, "IPC$")) set_share_flag(share, KSMBD_SHARE_FLAG_PIPE); g_hash_table_foreach(group->kv, process_group_kv, share); fixup_missing_fields(share); } int shm_add_new_share(struct smbconf_group *group) { int ret = 0; struct ksmbd_share *share = new_ksmbd_share(); if (!share) return -ENOMEM; init_share_from_group(share, group); if (test_share_flag(share, KSMBD_SHARE_FLAG_INVALID)) { pr_err("Invalid share %s\n", share->name); kill_ksmbd_share(share); return 0; } g_rw_lock_writer_lock(&shares_table_lock); if (__shm_lookup_share(share->name)) { g_rw_lock_writer_unlock(&shares_table_lock); pr_info("share exists %s\n", share->name); kill_ksmbd_share(share); return 0; } if (!g_hash_table_insert(shares_table, share->name, share)) { kill_ksmbd_share(share); ret = -EINVAL; } g_rw_lock_writer_unlock(&shares_table_lock); return ret; } int shm_lookup_users_map(struct ksmbd_share *share, enum share_users map, char *name) { int ret = -ENOENT; if (map >= KSMBD_SHARE_USERS_MAX) { pr_err("Invalid users map index: %d\n", map); return 0; } if (!share->maps[map]) return -EINVAL; g_rw_lock_reader_lock(&share->maps_lock); if (g_hash_table_lookup(share->maps[map], name)) ret = 0; g_rw_lock_reader_unlock(&share->maps_lock); return ret; } /* * FIXME * Do a real hosts lookup. IP masks, etc. */ int shm_lookup_hosts_map(struct ksmbd_share *share, enum share_hosts map, char *host) { GHashTable *lookup_map; int ret = -ENOENT; if (map >= KSMBD_SHARE_HOSTS_MAX) { pr_err("Invalid hosts map index: %d\n", map); return 0; } if (map == KSMBD_SHARE_HOSTS_ALLOW_MAP) lookup_map = share->hosts_allow_map; if (map == KSMBD_SHARE_HOSTS_DENY_MAP) lookup_map = share->hosts_deny_map; if (!lookup_map) return -EINVAL; g_rw_lock_reader_lock(&share->maps_lock); if (g_hash_table_lookup(lookup_map, host)) ret = 0; g_rw_lock_reader_unlock(&share->maps_lock); return ret; } int shm_open_connection(struct ksmbd_share *share) { int ret = 0; g_rw_lock_writer_lock(&share->update_lock); share->num_connections++; if (share->max_connections) { if (share->num_connections >= share->max_connections) ret = -EINVAL; } g_rw_lock_writer_unlock(&share->update_lock); return ret; } int shm_close_connection(struct ksmbd_share *share) { if (!share) return 0; g_rw_lock_writer_lock(&share->update_lock); share->num_connections--; g_rw_lock_writer_unlock(&share->update_lock); return 0; } void for_each_ksmbd_share(walk_shares cb, gpointer user_data) { g_rw_lock_reader_lock(&shares_table_lock); g_hash_table_foreach(shares_table, cb, user_data); g_rw_lock_reader_unlock(&shares_table_lock); } int shm_share_config_payload_size(struct ksmbd_share *share) { int sz = 1; if (share && !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE)) { if (share->path) sz += strlen(share->path); if (global_conf.root_dir) sz += strlen(global_conf.root_dir) + 1; if (share->veto_list_sz) sz += share->veto_list_sz + 1; } return sz; } int shm_handle_share_config_request(struct ksmbd_share *share, struct ksmbd_share_config_response *resp) { unsigned char *config_payload; if (!share) return -EINVAL; resp->flags = share->flags; resp->create_mask = share->create_mask; resp->directory_mask = share->directory_mask; resp->force_create_mode = share->force_create_mode; resp->force_directory_mode = share->force_directory_mode; resp->force_uid = share->force_uid; resp->force_gid = share->force_gid; resp->veto_list_sz = share->veto_list_sz; if (test_share_flag(share, KSMBD_SHARE_FLAG_PIPE)) return 0; if (!share->path) return 0; config_payload = KSMBD_SHARE_CONFIG_VETO_LIST(resp); if (resp->veto_list_sz) { memcpy(config_payload, share->veto_list, resp->veto_list_sz); config_payload += resp->veto_list_sz + 1; } if (global_conf.root_dir) sprintf(config_payload, "%s%s", global_conf.root_dir, share->path); else sprintf(config_payload, "%s", share->path); return 0; } ksmbd-tools-3.4.4/lib/management/spnego.c000066400000000000000000000200461417226526000203100ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 LG Electronics * * linux-cifsd-devel@lists.sourceforge.net */ #include "ksmbdtools.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include "spnego_mech.h" static struct spnego_mech_ctx mech_ctxs[SPNEGO_MAX_MECHS]; static struct spnego_mech_ctx *get_mech(int mech_type) { if (mech_type >= SPNEGO_MAX_MECHS) return NULL; return &mech_ctxs[mech_type]; } int spnego_init(void) { struct spnego_mech_ctx *mech_ctx; int i; mech_ctx = &mech_ctxs[SPNEGO_MECH_MSKRB5]; mech_ctx->ops = &spnego_mskrb5_operations; if (global_conf.krb5_service_name) mech_ctx->params.krb5.service_name = strdup(global_conf.krb5_service_name); if (global_conf.krb5_keytab_file) mech_ctx->params.krb5.keytab_name = strdup(global_conf.krb5_keytab_file); mech_ctx = &mech_ctxs[SPNEGO_MECH_KRB5]; mech_ctx->ops = &spnego_krb5_operations; if (global_conf.krb5_service_name) mech_ctx->params.krb5.service_name = strdup(global_conf.krb5_service_name); if (global_conf.krb5_keytab_file) mech_ctx->params.krb5.keytab_name = strdup(global_conf.krb5_keytab_file); for (i = 0; i < SPNEGO_MAX_MECHS; i++) { if (mech_ctxs[i].ops->setup && mech_ctxs[i].ops->setup(&mech_ctxs[i])) { pr_err("Failed to init Kerberos\n"); goto out_err; } } return 0; out_err: for (; i >= 0; i--) { if (mech_ctxs[i].ops->cleanup) mech_ctxs[i].ops->cleanup(&mech_ctxs[i]); } return -ENOTSUP; } void spnego_destroy(void) { int i; for (i = 0; i < SPNEGO_MAX_MECHS; i++) { if (mech_ctxs[i].ops->cleanup) mech_ctxs[i].ops->cleanup(&mech_ctxs[i]); } } static int compare_oid(unsigned long *oid1, unsigned int oid1len, unsigned long *oid2, unsigned int oid2len) { unsigned int i; if (oid1len != oid2len) return 1; for (i = 0; i < oid1len; i++) { if (oid1[i] != oid2[i]) return 1; } return 0; } static bool is_supported_mech(unsigned long *oid, unsigned int len, int *mech_type) { if (!compare_oid(oid, len, MSKRB5_OID, ARRAY_SIZE(MSKRB5_OID))) { *mech_type = SPNEGO_MECH_MSKRB5; return true; } else if (!compare_oid(oid, len, KRB5_OID, ARRAY_SIZE(KRB5_OID))) { *mech_type = SPNEGO_MECH_KRB5; return true; } *mech_type = SPNEGO_MAX_MECHS; return false; } static int decode_asn1_header(struct asn1_ctx *ctx, unsigned char **end, unsigned int cls, unsigned int con, unsigned int tag) { unsigned int d_cls, d_con, d_tag; if (asn1_header_decode(ctx, end, &d_cls, &d_con, &d_tag) == 0 || (d_cls != cls || d_con != con || d_tag != tag)) return -EINVAL; return 0; } static int decode_negTokenInit(unsigned char *negToken, int token_len, int *mech_type, unsigned char **krb5_ap_req, unsigned int *req_len) { struct asn1_ctx ctx; unsigned char *end, *mech_types_end, *id; unsigned long *oid = NULL; unsigned int len; asn1_open(&ctx, negToken, token_len); /* GSSAPI header */ if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) { pr_debug("Error decoding SPNEGO application tag\n"); return -EINVAL; } /* SPNEGO oid */ if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) || asn1_oid_decode(&ctx, end, &oid, &len) == 0 || compare_oid(oid, len, SPNEGO_OID, SPNEGO_OID_LEN)) { pr_debug("Error decoding SPNEGO oid\n"); free(oid); return -EINVAL; } free(oid); /* negoTokenInit */ if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) || decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_CON, ASN1_SEQ)) { pr_debug("Error decoding negTokenInit tag\n"); return -EINVAL; } /* mechTypes */ if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) || decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_CON, ASN1_SEQ)) { pr_debug("Error decoding mechTypes tag\n"); return -EINVAL; } mech_types_end = end; if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) || asn1_oid_decode(&ctx, end, &oid, &len) == 0) { pr_debug("Error decoding Kerberos oids\n"); return -EINVAL; } if (!is_supported_mech(oid, len, mech_type)) { free(oid); pr_debug("Not support mechanism\n"); return -EINVAL; } free(oid); ctx.pointer = mech_types_end; /* mechToken */ if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 2) || decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OTS)) { pr_debug("Error decoding krb5_blob\n"); return -EINVAL; } if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) { pr_debug("Error decoding GSSAPI application tag\n"); return -EINVAL; } /* Kerberos 5 oid */ if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI)) { pr_debug("Error decoding Kerberos oid tag\n"); return -EINVAL; } if (asn1_oid_decode(&ctx, end, &oid, &len) == 0 || compare_oid(oid, len, KRB5_OID, ARRAY_SIZE(KRB5_OID))) { pr_debug("not Kerberos OID\n"); free(oid); return -EINVAL; } free(oid); /* AP_REQ id */ if (asn1_read(&ctx, &id, 2) == 0 || id[0] != 1 || id[1] != 0) { if (id) free(id); pr_debug("Error decoding AP_REQ id\n"); return -EINVAL; } free(id); /* AP_REQ */ *req_len = (unsigned int)(ctx.end - ctx.pointer); *krb5_ap_req = ctx.pointer; return 0; } static int encode_negTokenTarg(char *in_blob, int in_len, const unsigned long *oid, int oid_len, char **out_blob, int *out_len) { unsigned char *buf; unsigned char *sup_oid, *krb5_oid; int sup_oid_len, krb5_oid_len; unsigned int neg_result_len, sup_mech_len, rep_token_len, len; if (asn1_oid_encode(oid, oid_len, &sup_oid, &sup_oid_len)) return -ENOMEM; if (asn1_oid_encode(KRB5_OID, ARRAY_SIZE(KRB5_OID), &krb5_oid, &krb5_oid_len)) { free(sup_oid); return -ENOMEM; } neg_result_len = asn1_header_len(1, 2); sup_mech_len = asn1_header_len(sup_oid_len, 2); rep_token_len = asn1_header_len(krb5_oid_len, 1); rep_token_len += 2 + in_len; rep_token_len = asn1_header_len(rep_token_len, 3); *out_len = asn1_header_len( neg_result_len + sup_mech_len + rep_token_len, 2); *out_blob = calloc(1, *out_len); if (*out_blob == NULL) return -ENOMEM; buf = *out_blob; /* negTokenTarg */ len = *out_len; asn1_header_encode(&buf, ASN1_CTX, ASN1_CON, 1, &len); asn1_header_encode(&buf, ASN1_UNI, ASN1_CON, ASN1_SEQ, &len); /* negTokenTarg/negResult */ len = neg_result_len; asn1_header_encode(&buf, ASN1_CTX, ASN1_CON, 0, &len); asn1_header_encode(&buf, ASN1_UNI, ASN1_PRI, ASN1_ENUM, &len); *buf++ = 0; /* negTokenTarg/supportedMechType */ len = sup_mech_len; asn1_header_encode(&buf, ASN1_CTX, ASN1_CON, 1, &len); asn1_header_encode(&buf, ASN1_UNI, ASN1_PRI, ASN1_OJI, &len); memcpy(buf, sup_oid, sup_oid_len); buf += len; /* negTokenTarg/responseToken */ len = rep_token_len; asn1_header_encode(&buf, ASN1_CTX, ASN1_CON, 2, &len); asn1_header_encode(&buf, ASN1_UNI, ASN1_PRI, ASN1_OTS, &len); asn1_header_encode(&buf, ASN1_APL, ASN1_CON, 0, &len); /* negTokenTarg/responseToken/OID */ len = asn1_header_len(krb5_oid_len, 1); asn1_header_encode(&buf, ASN1_UNI, ASN1_PRI, ASN1_OJI, &len); /* negTokenTarg/responseToken/mechToken*/ memcpy(buf, krb5_oid, krb5_oid_len); buf += len; /* AP_REP id */ *buf++ = 2; *buf++ = 0; memcpy(buf, in_blob, in_len); free(sup_oid); free(krb5_oid); } int spnego_handle_authen_request(struct ksmbd_spnego_authen_request *req, struct ksmbd_spnego_auth_out *auth_out) { struct spnego_mech_ctx *mech_ctx; unsigned char *mech_token; int token_len, mech_type; int retval = 0; if (decode_negTokenInit(req->spnego_blob, (int)req->spnego_blob_len, &mech_type, &mech_token, &token_len)) { pr_info("Error decoding negTokenInit\n"); return -EINVAL; } mech_ctx = get_mech(mech_type); if (!mech_ctx) { retval = -ENOTSUP; pr_info("Not support Kerberos\n"); goto out; } if (mech_ctx->ops->handle_authen(mech_ctx, mech_token, token_len, auth_out, encode_negTokenTarg)) { retval = -EPERM; pr_info("Error authenticate\n"); goto out; } out: return retval; } ksmbd-tools-3.4.4/lib/management/spnego_krb5.c000066400000000000000000000213371417226526000212370ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 LG Electronics * * linux-cifsd-devel@lists.sourceforge.net */ #include "ksmbdtools.h" #include #include #include #include #include #include #include #include #include #include "spnego_mech.h" struct spnego_krb5_ctx { krb5_context context; krb5_keytab keytab; krb5_creds creds; }; #define SERVICE_NAME "cifs" #define pr_krb5_err(_context, _retval, _fmt, ...) \ do { \ const char *msg = krb5_get_error_message(_context, _retval); \ pr_err("%s: " _fmt, msg, ##__VA_ARGS__); \ krb5_free_error_message(_context, msg); \ } while (0) static char *get_service_name(void) { return strdup(SERVICE_NAME); } static char *get_host_name(void) { struct addrinfo hint, *ai; char *host_name; char hostname[NI_MAXHOST]; if (gethostname(hostname, sizeof(hostname))) return NULL; memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_UNSPEC; hint.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; if (getaddrinfo(hostname, NULL, &hint, &ai)) return NULL; host_name = strdup(ai->ai_canonname); freeaddrinfo(ai); return host_name; } /* Service full name is [/[@REALM>]] */ static int parse_service_full_name(char *service_full_name, char **service_name, char **host_name) { char *name, *delim; *service_name = NULL; *host_name = NULL; if (!service_full_name) { *service_name = get_service_name(); *host_name = get_host_name(); goto out; } name = service_full_name; delim = strchr(name, '/'); if (!delim) { *service_name = strdup(name); *host_name = get_host_name(); goto out; } *service_name = strndup(name, delim - name); if (*service_name == NULL) return -ENOMEM; name = delim + 1; delim = strchr(name, '@'); if (!delim) { *host_name = strdup(name); goto out; } *host_name = strndup(name, delim - name); if (*host_name == NULL) { free(*service_name); return -ENOMEM; } out: /* we assume the host name is FQDN if it has "." */ if (strchr(*host_name, '.')) return 0; free(*service_name); free(*host_name); *service_name = NULL; *host_name = NULL; return -EINVAL; } static krb5_error_code acquire_creds_from_keytab(krb5_context context, char *service_full_name, char *keytab_name, krb5_creds *out_creds, krb5_keytab *keytab) { krb5_error_code retval; krb5_principal sprinc = NULL; char *host_name = NULL, *service_name = NULL; if (keytab_name) retval = krb5_kt_resolve(context, keytab_name, keytab); else retval = krb5_kt_default(context, keytab); if (retval) { pr_krb5_err(context, retval, "while resolving keytab\n"); return retval; } if (parse_service_full_name(service_full_name, &service_name, &host_name)) { retval = KRB5_ERR_HOST_REALM_UNKNOWN; pr_krb5_err(context, retval, "while getting host name\n"); goto out_err; } retval = krb5_sname_to_principal(context, host_name, service_name, KRB5_NT_UNKNOWN, &sprinc); if (retval) { pr_krb5_err(context, retval, "while generating service name\n"); goto out_err; } retval = krb5_get_init_creds_keytab(context, out_creds, sprinc, *keytab, 0, NULL, NULL); if (retval) { char *name; krb5_unparse_name(context, sprinc, &name); pr_krb5_err(context, retval, "while getting credentails for %s\n", name); krb5_free_unparsed_name(context, name); goto out_err; } free(host_name); free(service_name); return 0; out_err: if (sprinc) krb5_free_principal(context, sprinc); if (service_name) free(service_name); if (host_name) free(host_name); if (*keytab) krb5_kt_close(context, *keytab); return retval; } static int handle_krb5_authen(struct spnego_mech_ctx *mech_ctx, char *in_blob, unsigned int in_len, struct ksmbd_spnego_auth_out *auth_out, spnego_encode_t spnego_encode) { struct spnego_krb5_ctx *krb5_ctx; char *client_name; krb5_auth_context auth_context; krb5_data packet, ap_rep; krb5_ticket *ticket = NULL; krb5_keyblock *session_key; krb5_authenticator *authenti; int retval = -EINVAL; krb5_error_code krb_retval; krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private; if (!krb5_ctx) return -EINVAL; krb_retval = krb5_auth_con_init(krb5_ctx->context, &auth_context); if (krb_retval) { pr_krb5_err(krb5_ctx->context, krb_retval, "while initailzing auth context\n"); return -EINVAL; } packet.length = in_len; packet.data = (krb5_pointer)in_blob; krb_retval = krb5_rd_req(krb5_ctx->context, &auth_context, &packet, krb5_ctx->creds.client, krb5_ctx->keytab, NULL, &ticket); if (krb_retval) { char *name; krb5_unparse_name(krb5_ctx->context, krb5_ctx->creds.client, &name); krb5_auth_con_free(krb5_ctx->context, auth_context); pr_krb5_err(krb5_ctx->context, krb_retval, "while decoding AP_REQ with %s creds\n", name); krb5_free_unparsed_name(krb5_ctx->context, name); return -EINVAL; } krb_retval = krb5_auth_con_getsendsubkey(krb5_ctx->context, auth_context, &session_key); if (krb_retval) { pr_krb5_err(krb5_ctx->context, krb_retval, "while reading session key\n"); goto out_free_con_auth; } krb_retval = krb5_mk_rep(krb5_ctx->context, auth_context, &ap_rep); if (krb_retval) { pr_krb5_err(krb5_ctx->context, krb_retval, "while making AP_REP\n"); goto out_free_key; } krb_retval = krb5_auth_con_getauthenticator(krb5_ctx->context, auth_context, &authenti); if (krb_retval) { pr_krb5_err(krb5_ctx->context, krb_retval, "while getting authenticator\n"); goto out_free_rep; } krb_retval = krb5_unparse_name_flags(krb5_ctx->context, authenti->client, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &client_name); if (krb_retval) { pr_krb5_err(krb5_ctx->context, krb_retval, "while unparsing client name\n"); goto out_free_auth; } memset(auth_out, 0, sizeof(*auth_out)); auth_out->user_name = strdup(client_name); if (!auth_out->user_name) { krb5_free_unparsed_name(krb5_ctx->context, client_name); retval = -ENOMEM; goto out_free_auth; } krb5_free_unparsed_name(krb5_ctx->context, client_name); auth_out->sess_key = malloc(session_key->length); if (!auth_out->sess_key) { free(auth_out->user_name); retval = -ENOMEM; goto out_free_auth; } memcpy(auth_out->sess_key, session_key->contents, session_key->length); auth_out->key_len = session_key->length; if (spnego_encode(ap_rep.data, ap_rep.length, mech_ctx->oid, mech_ctx->oid_len, &auth_out->spnego_blob, &auth_out->blob_len)) { free(auth_out->user_name); free(auth_out->sess_key); goto out_free_auth; } pr_info("Succeeded to authenticate %s\n", auth_out->user_name); retval = 0; out_free_auth: krb5_free_authenticator(krb5_ctx->context, authenti); out_free_rep: krb5_free_data_contents(krb5_ctx->context, &ap_rep); out_free_key: krb5_free_keyblock(krb5_ctx->context, session_key); out_free_con_auth: krb5_free_ticket(krb5_ctx->context, ticket); krb5_auth_con_free(krb5_ctx->context, auth_context); return retval; } static int setup_krb5_ctx(struct spnego_mech_ctx *mech_ctx) { struct spnego_krb5_ctx *krb5_ctx; krb5_error_code krb_retval; krb5_ctx = calloc(1, sizeof(*krb5_ctx)); if (!krb5_ctx) return -ENOMEM; krb_retval = krb5_init_context(&krb5_ctx->context); if (krb_retval) { free(krb5_ctx); pr_err("while initializing krb5 context"); return -EINVAL; } krb_retval = acquire_creds_from_keytab(krb5_ctx->context, mech_ctx->params.krb5.service_name, mech_ctx->params.krb5.keytab_name, &krb5_ctx->creds, &krb5_ctx->keytab); if (krb_retval) { krb5_free_context(krb5_ctx->context); free(krb5_ctx); return -EINVAL; } mech_ctx->private = krb5_ctx; return 0; } static int setup_krb5(struct spnego_mech_ctx *mech_ctx) { mech_ctx->oid = KRB5_OID; mech_ctx->oid_len = ARRAY_SIZE(KRB5_OID); return setup_krb5_ctx(mech_ctx); } static int setup_mskrb5(struct spnego_mech_ctx *mech_ctx) { mech_ctx->oid = MSKRB5_OID; mech_ctx->oid_len = ARRAY_SIZE(MSKRB5_OID); return setup_krb5_ctx(mech_ctx); } static void cleanup_krb5(struct spnego_mech_ctx *mech_ctx) { if (mech_ctx->private) { struct spnego_krb5_ctx *krb5_ctx; krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private; krb5_free_cred_contents(krb5_ctx->context, &krb5_ctx->creds); krb5_kt_close(krb5_ctx->context, krb5_ctx->keytab); krb5_free_context(krb5_ctx->context); free(krb5_ctx); mech_ctx->private = NULL; } if (mech_ctx->params.krb5.service_name) free(mech_ctx->params.krb5.service_name); if (mech_ctx->params.krb5.keytab_name) free(mech_ctx->params.krb5.keytab_name); } struct spnego_mech_operations spnego_krb5_operations = { .setup = setup_krb5, .cleanup = cleanup_krb5, .handle_authen = handle_krb5_authen, }; struct spnego_mech_operations spnego_mskrb5_operations = { .setup = setup_mskrb5, .cleanup = cleanup_krb5, .handle_authen = handle_krb5_authen, }; ksmbd-tools-3.4.4/lib/management/spnego_mech.h000066400000000000000000000020701417226526000213060ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2020 LG Electronics * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef _SPNEGO_MECH_H_ #define _SPNEGO_MECH_H_ enum { SPNEGO_MECH_MSKRB5 = 0, SPNEGO_MECH_KRB5, SPNEGO_MAX_MECHS, }; struct spnego_mech_ctx; typedef int (*spnego_encode_t)(char *in_blob, int in_len, const unsigned long *oid, int oid_len, char **out_blob, int *out_len); struct spnego_mech_operations { int (*setup)(struct spnego_mech_ctx *mech_ctx); void (*cleanup)(struct spnego_mech_ctx *mech_ctx); int (*handle_authen)(struct spnego_mech_ctx *mech_ctx, char *in_blob, unsigned int in_len, struct ksmbd_spnego_auth_out *auth_out, spnego_encode_t encode); }; struct spnego_mech_ctx { const unsigned long *oid; int oid_len; void *private; union { struct { void *keytab_name; void *service_name; } krb5; } params; struct spnego_mech_operations *ops; }; extern struct spnego_mech_operations spnego_krb5_operations; extern struct spnego_mech_operations spnego_mskrb5_operations; #endif ksmbd-tools-3.4.4/lib/management/tree_conn.c000066400000000000000000000134031417226526000207700ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include "linux/ksmbd_server.h" #include "management/tree_conn.h" #include "management/session.h" #include "management/share.h" #include "management/user.h" #include "ksmbdtools.h" static struct ksmbd_tree_conn *new_ksmbd_tree_conn(void) { struct ksmbd_tree_conn *conn; conn = calloc(1, sizeof(struct ksmbd_tree_conn)); if (!conn) return NULL; conn->id = 0; return conn; } void tcm_tree_conn_free(struct ksmbd_tree_conn *conn) { shm_close_connection(conn->share); put_ksmbd_share(conn->share); free(conn); } int tcm_handle_tree_connect(struct ksmbd_tree_connect_request *req, struct ksmbd_tree_connect_response *resp) { struct ksmbd_user *user = NULL; struct ksmbd_share *share = NULL; struct ksmbd_tree_conn *conn = new_ksmbd_tree_conn(); int ret; if (!conn) { resp->status = KSMBD_TREE_CONN_STATUS_NOMEM; return -ENOMEM; } if (sm_check_sessions_capacity(req->session_id)) { resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS; pr_debug("treecon: Too many active sessions\n"); goto out_error; } if (global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER) { if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) { resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER; pr_debug("treecon: Bad user password\n"); goto out_error; } } share = shm_lookup_share(req->share); if (!share) { resp->status = KSMBD_TREE_CONN_STATUS_NO_SHARE; pr_err("treecon: unknown net share: %s\n", req->share); goto out_error; } if (test_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE)) set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE); if (test_share_flag(share, KSMBD_SHARE_FLAG_READONLY)) set_conn_flag(conn, KSMBD_SHARE_FLAG_READONLY); if (shm_open_connection(share)) { resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS; pr_debug("treecon: Too many connections to a net share\n"); goto out_error; } ret = shm_lookup_hosts_map(share, KSMBD_SHARE_HOSTS_ALLOW_MAP, req->peer_addr); if (ret == -ENOENT) { resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED; pr_debug("treecon: host denied: %s\n", req->peer_addr); goto out_error; } if (ret != 0) { ret = shm_lookup_hosts_map(share, KSMBD_SHARE_HOSTS_DENY_MAP, req->peer_addr); if (ret == 0) { resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED; pr_err("treecon: host denied: %s\n", req->peer_addr); goto out_error; } } if (global_conf.restrict_anon >= KSMBD_RESTRICT_ANON_TYPE_1) { int deny; deny = !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK); deny |= test_share_flag(share, KSMBD_SHARE_FLAG_PIPE); if (req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT && deny) { pr_debug("treecon: deny. Restricted session\n"); resp->status = KSMBD_TREE_CONN_STATUS_ERROR; goto out_error; } } if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) && !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE) && !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) { pr_debug("treecon: deny. Not allow guest\n"); resp->status = KSMBD_TREE_CONN_STATUS_ERROR; goto out_error; } if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) && test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) { pr_debug("treecon: net share permits guest login\n"); user = usm_lookup_user(share->guest_account); if (user) { set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT); goto bind; } user = usm_lookup_user(global_conf.guest_account); if (user) { set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT); goto bind; } } user = usm_lookup_user(req->account); if (!user) { resp->status = KSMBD_TREE_CONN_STATUS_NO_USER; pr_err("treecon: user account not found: %s\n", req->account); goto out_error; } user->failed_login_count = 0; user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION; if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT)) set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT); ret = shm_lookup_users_map(share, KSMBD_SHARE_ADMIN_USERS_MAP, req->account); if (ret == 0) { set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT); goto bind; } ret = shm_lookup_users_map(share, KSMBD_SHARE_INVALID_USERS_MAP, req->account); if (ret == 0) { resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER; pr_err("treecon: user is on invalid list\n"); goto out_error; } ret = shm_lookup_users_map(share, KSMBD_SHARE_READ_LIST_MAP, req->account); if (ret == 0) { set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_READ_ONLY); clear_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE); goto bind; } ret = shm_lookup_users_map(share, KSMBD_SHARE_WRITE_LIST_MAP, req->account); if (ret == 0) { set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE); goto bind; } ret = shm_lookup_users_map(share, KSMBD_SHARE_VALID_USERS_MAP, req->account); if (ret == 0) goto bind; if (ret == -ENOENT) { resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER; pr_err("treecon: user is not on the valid list\n"); goto out_error; } bind: conn->id = req->connect_id; conn->share = share; resp->status = KSMBD_TREE_CONN_STATUS_OK; resp->connection_flags = conn->flags; if (sm_handle_tree_connect(req->session_id, user, conn)) { pr_err("ERROR: we were unable to bind tree connection\n"); tcm_tree_conn_free(conn); put_ksmbd_user(user); } return 0; out_error: tcm_tree_conn_free(conn); shm_close_connection(share); put_ksmbd_share(share); put_ksmbd_user(user); return -EINVAL; } int tcm_handle_tree_disconnect(unsigned long long sess_id, unsigned long long tree_conn_id) { sm_handle_tree_disconnect(sess_id, tree_conn_id); return 0; } ksmbd-tools-3.4.4/lib/management/user.c000066400000000000000000000202361417226526000177740ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include "linux/ksmbd_server.h" #include "management/user.h" #include "ksmbdtools.h" #define KSMBD_USER_STATE_FREEING 1 static GHashTable *users_table; static GRWLock users_table_lock; static void kill_ksmbd_user(struct ksmbd_user *user) { pr_debug("Kill user %s\n", user->name); free(user->name); free(user->pass_b64); free(user->pass); g_rw_lock_clear(&user->update_lock); free(user); } static int __usm_remove_user(struct ksmbd_user *user) { int ret = 0; if (user->state != KSMBD_USER_STATE_FREEING) { g_rw_lock_writer_lock(&users_table_lock); if (!g_hash_table_remove(users_table, user->name)) ret = -EINVAL; g_rw_lock_writer_unlock(&users_table_lock); } if (!ret) kill_ksmbd_user(user); return ret; } struct ksmbd_user *get_ksmbd_user(struct ksmbd_user *user) { g_rw_lock_writer_lock(&user->update_lock); if (user->ref_count != 0) { user->ref_count++; g_rw_lock_writer_unlock(&user->update_lock); } else { g_rw_lock_writer_unlock(&user->update_lock); user = NULL; } return user; } void put_ksmbd_user(struct ksmbd_user *user) { int drop; if (!user) return; g_rw_lock_writer_lock(&user->update_lock); user->ref_count--; drop = !user->ref_count; g_rw_lock_writer_unlock(&user->update_lock); if (!drop) return; __usm_remove_user(user); } static gboolean put_user_callback(gpointer _k, gpointer _v, gpointer data) { struct ksmbd_user *user = (struct ksmbd_user *)_v; user->state = KSMBD_USER_STATE_FREEING; put_ksmbd_user(user); return TRUE; } void usm_remove_all_users(void) { g_rw_lock_writer_lock(&users_table_lock); g_hash_table_foreach_remove(users_table, put_user_callback, NULL); g_rw_lock_writer_unlock(&users_table_lock); } static struct ksmbd_user *new_ksmbd_user(char *name, char *pwd) { struct ksmbd_user *user; struct passwd *passwd; size_t pass_sz; user = calloc(1, sizeof(struct ksmbd_user)); if (!user) return NULL; g_rw_lock_init(&user->update_lock); user->name = name; user->pass_b64 = pwd; user->ref_count = 1; user->gid = 9999; user->uid = 9999; passwd = getpwnam(name); if (passwd) { user->uid = passwd->pw_uid; user->gid = passwd->pw_gid; } user->pass = base64_decode(user->pass_b64, &pass_sz); user->pass_sz = (int)pass_sz; return user; } static void free_hash_entry(gpointer k, gpointer u, gpointer user_data) { kill_ksmbd_user(u); } static void usm_clear_users(void) { g_hash_table_foreach(users_table, free_hash_entry, NULL); } void usm_destroy(void) { if (users_table) { usm_clear_users(); g_hash_table_destroy(users_table); } g_rw_lock_clear(&users_table_lock); } int usm_init(void) { users_table = g_hash_table_new(g_str_hash, g_str_equal); if (!users_table) return -ENOMEM; g_rw_lock_init(&users_table_lock); return 0; } static struct ksmbd_user *__usm_lookup_user(char *name) { return g_hash_table_lookup(users_table, name); } struct ksmbd_user *usm_lookup_user(char *name) { struct ksmbd_user *user, *ret; if (!name) return NULL; g_rw_lock_reader_lock(&users_table_lock); user = __usm_lookup_user(name); if (user) { ret = get_ksmbd_user(user); if (!ret) user = NULL; } g_rw_lock_reader_unlock(&users_table_lock); return user; } int usm_add_new_user(char *name, char *pwd) { int ret = 0; struct ksmbd_user *user = new_ksmbd_user(name, pwd); if (!user) { free(name); free(pwd); return -ENOMEM; } g_rw_lock_writer_lock(&users_table_lock); if (__usm_lookup_user(name)) { g_rw_lock_writer_unlock(&users_table_lock); pr_info("User already exists %s\n", name); kill_ksmbd_user(user); return 0; } if (!g_hash_table_insert(users_table, user->name, user)) { kill_ksmbd_user(user); ret = -EINVAL; } g_rw_lock_writer_unlock(&users_table_lock); return ret; } int usm_add_update_user_from_pwdentry(char *data) { struct ksmbd_user *user; char *name; char *pwd; char *pos = strchr(data, ':'); int ret; if (!pos) { pr_err("Invalid pwd entry %s\n", data); return -EINVAL; } *pos = 0x00; name = g_strdup(data); pwd = g_strdup(pos + 1); if (!name || !pwd) { free(name); free(pwd); return -ENOMEM; } user = usm_lookup_user(name); if (user) { ret = usm_update_user_password(user, pwd); put_ksmbd_user(user); free(name); free(pwd); return ret; } return usm_add_new_user(name, pwd); } int usm_add_subauth_global_conf(char *data) { char *pos = data; char *spos; if (!pos) return -EINVAL; spos = strchr(pos, ':'); if (!spos) { pr_err("Invalid subauth entry %s\n", data); return -EINVAL; } *spos = 0x00; global_conf.gen_subauth[0] = atoi(pos); pos = spos + 1; spos = strchr(pos, ':'); if (!spos) { pr_err("Invalid subauth entry %s\n", data); return -EINVAL; } *spos = 0x00; global_conf.gen_subauth[1] = atoi(pos); global_conf.gen_subauth[2] = atoi(spos + 1); return 0; } void for_each_ksmbd_user(walk_users cb, gpointer user_data) { g_rw_lock_reader_lock(&users_table_lock); g_hash_table_foreach(users_table, cb, user_data); g_rw_lock_reader_unlock(&users_table_lock); } int usm_update_user_password(struct ksmbd_user *user, char *pswd) { size_t pass_sz; char *pass_b64 = g_strdup(pswd); char *pass = base64_decode(pass_b64, &pass_sz); if (!pass_b64 || !pass) { free(pass_b64); free(pass); pr_err("Cannot allocate new user entry: out of memory\n"); return -ENOMEM; } pr_debug("Update user password: %s\n", user->name); g_rw_lock_writer_lock(&user->update_lock); free(user->pass_b64); free(user->pass); user->pass_b64 = pass_b64; user->pass = pass; user->pass_sz = (int)pass_sz; g_rw_lock_writer_unlock(&user->update_lock); return 0; } static int usm_copy_user_passhash(struct ksmbd_user *user, char *pass, size_t sz) { int ret = -ENOSPC; if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT)) return 0; g_rw_lock_reader_lock(&user->update_lock); if (sz >= user->pass_sz) { memcpy(pass, user->pass, user->pass_sz); ret = user->pass_sz; } g_rw_lock_reader_unlock(&user->update_lock); return ret; } static int usm_copy_user_account(struct ksmbd_user *user, char *account, size_t sz) { int account_sz; if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT)) return 0; account_sz = strlen(user->name); if (sz >= account_sz) { memcpy(account, user->name, account_sz); return 0; } pr_err("Cannot copy user data, buffer overrun\n"); return -ENOSPC; } static void __handle_login_request(struct ksmbd_login_response *resp, struct ksmbd_user *user) { int hash_sz; resp->gid = user->gid; resp->uid = user->uid; resp->status = user->flags; resp->status |= KSMBD_USER_FLAG_OK; hash_sz = usm_copy_user_passhash(user, resp->hash, sizeof(resp->hash)); if (hash_sz < 0) { resp->status = KSMBD_USER_FLAG_INVALID; } else { resp->hash_sz = (unsigned short)hash_sz; if (usm_copy_user_account(user, resp->account, sizeof(resp->account))) resp->status = KSMBD_USER_FLAG_INVALID; } } int usm_handle_login_request(struct ksmbd_login_request *req, struct ksmbd_login_response *resp) { struct ksmbd_user *user = NULL; int null_session = 0; if (req->account[0] == '\0') null_session = 1; if (!null_session) user = usm_lookup_user(req->account); if (user) { __handle_login_request(resp, user); put_ksmbd_user(user); return 0; } resp->status = KSMBD_USER_FLAG_BAD_USER; if (!null_session && global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER) return 0; if (null_session || global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_BAD_USER) user = usm_lookup_user(global_conf.guest_account); if (!user) return 0; __handle_login_request(resp, user); put_ksmbd_user(user); return 0; } int usm_handle_logout_request(struct ksmbd_logout_request *req) { struct ksmbd_user *user; user = usm_lookup_user(req->account); if (!user) return -ENOENT; if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) { if (user->failed_login_count < 10) user->failed_login_count++; else user->flags |= KSMBD_USER_FLAG_DELAY_SESSION; } else { user->failed_login_count = 0; user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION; } } ksmbd-tools-3.4.4/mountd/000077500000000000000000000000001417226526000152735ustar00rootroot00000000000000ksmbd-tools-3.4.4/mountd/Makefile.am000066400000000000000000000005241417226526000173300ustar00rootroot00000000000000AM_CFLAGS = -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common LIBS = $(GLIB_LIBS) $(LIBNL_LIBS) $(LIBKRB5_LIBS) ksmbd_mountd_LDADD = $(top_builddir)/lib/libksmbdtools.a sbin_PROGRAMS = ksmbd.mountd ksmbd_mountd_SOURCES = worker.c ipc.c rpc.c rpc_srvsvc.c rpc_wkssvc.c mountd.c \ smbacl.c rpc_samr.c rpc_lsarpc.c ksmbd-tools-3.4.4/mountd/ipc.c000066400000000000000000000270751417226526000162250ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct nl_sock *sk; struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) { struct ksmbd_ipc_msg *msg; size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg) + 1; if (msg_sz > KSMBD_IPC_MAX_MESSAGE_SIZE) pr_err("IPC message is too large: %zu\n", msg_sz); msg = calloc(1, msg_sz); if (msg) msg->sz = sz; return msg; } void ipc_msg_free(struct ksmbd_ipc_msg *msg) { free(msg); } static int generic_event(int type, void *payload, size_t sz) { struct ksmbd_ipc_msg *event; event = ipc_msg_alloc(sz); if (!event) return -ENOMEM; event->type = type; event->sz = sz; memcpy(KSMBD_IPC_MSG_PAYLOAD(event), payload, sz); wp_ipc_msg_push(event); return 0; } static int parse_reload_configs(const char *pwddb, const char *smbconf) { int ret; pr_debug("Reload config\n"); usm_remove_all_users(); ret = cp_parse_pwddb(pwddb); if (ret == -ENOENT) { pr_err("User database file does not exist. %s\n", "Only guest sessions (if permitted) will work."); } else if (ret) { pr_err("Unable to parse user database\n"); return ret; } shm_remove_all_shares(); ret = cp_parse_reload_smbconf(smbconf); if (ret) pr_err("Unable to parse smb configuration file\n"); return ret; } static int handle_generic_event(struct nl_cache_ops *unused, struct genl_cmd *cmd, struct genl_info *info, void *arg) { if (ksmbd_health_status & KSMBD_SHOULD_RELOAD_CONFIG) { parse_reload_configs(global_conf.pwddb, global_conf.smbconf); ksmbd_health_status &= ~KSMBD_SHOULD_RELOAD_CONFIG; } if (!info->attrs[cmd->c_id]) return NL_SKIP; return generic_event(cmd->c_id, nla_data(info->attrs[cmd->c_id]), nla_len(info->attrs[cmd->c_id])); } static int nlink_msg_cb(struct nl_msg *msg, void *arg) { struct genlmsghdr *gnlh = genlmsg_hdr(nlmsg_hdr(msg)); if (gnlh->version != KSMBD_GENL_VERSION) { pr_err("IPC message version mistamtch: %d\n", gnlh->version); return NL_SKIP; } #if TRACING_DUMP_NL_MSG nl_msg_dump(msg, stdout); #endif return genl_handle_msg(msg, NULL); } static int handle_unsupported_event(struct nl_cache_ops *unused, struct genl_cmd *cmd, struct genl_info *info, void *arg) { pr_err("Unsupported IPC event %d, ignore.\n", cmd->c_id); return NL_SKIP; } static int ifc_list_size(void) { int len = 0; int i; for (i = 0; global_conf.interfaces[i] != NULL; i++) { char *ifc = global_conf.interfaces[i]; ifc = cp_ltrim(ifc); if (!ifc) continue; len += strlen(ifc) + 1; } return len; } static int ipc_ksmbd_starting_up(void) { struct ksmbd_startup_request *ev; struct ksmbd_ipc_msg *msg; int ifc_list_sz = 0; int ret; if (global_conf.bind_interfaces_only && global_conf.interfaces) ifc_list_sz += ifc_list_size(); msg = ipc_msg_alloc(sizeof(*ev) + ifc_list_sz); if (!msg) return -ENOMEM; ev = KSMBD_IPC_MSG_PAYLOAD(msg); msg->type = KSMBD_EVENT_STARTING_UP; ev->flags = global_conf.flags; ev->signing = global_conf.server_signing; ev->tcp_port = global_conf.tcp_port; ev->ipc_timeout = global_conf.ipc_timeout; ev->deadtime = global_conf.deadtime; ev->file_max = global_conf.file_max; ev->smb2_max_read = global_conf.smb2_max_read; ev->smb2_max_write = global_conf.smb2_max_write; ev->smb2_max_trans = global_conf.smb2_max_trans; ev->share_fake_fscaps = global_conf.share_fake_fscaps; memcpy(ev->sub_auth, global_conf.gen_subauth, sizeof(ev->sub_auth)); ev->smb2_max_credits = global_conf.smb2_max_credits; if (global_conf.server_min_protocol) { strncpy(ev->min_prot, global_conf.server_min_protocol, sizeof(ev->min_prot) - 1); } if (global_conf.server_max_protocol) { strncpy(ev->max_prot, global_conf.server_max_protocol, sizeof(ev->max_prot) - 1); } if (global_conf.netbios_name) { strncpy(ev->netbios_name, global_conf.netbios_name, sizeof(ev->netbios_name) - 1); } if (global_conf.server_string) { strncpy(ev->server_string, global_conf.server_string, sizeof(ev->server_string) - 1); } if (global_conf.work_group) { strncpy(ev->work_group, global_conf.work_group, sizeof(ev->work_group) - 1); } if (ifc_list_sz) { int i; int sz = 0; char *config_payload = KSMBD_STARTUP_CONFIG_INTERFACES(ev); ev->ifc_list_sz = ifc_list_sz; for (i = 0; global_conf.interfaces[i] != NULL; i++) { char *ifc = global_conf.interfaces[i]; ifc = cp_ltrim(ifc); if (!ifc) continue; strcpy(config_payload + sz, ifc); sz += strlen(ifc) + 1; } global_conf.bind_interfaces_only = 0; cp_group_kv_list_free(global_conf.interfaces); } ret = ipc_msg_send(msg); ipc_msg_free(msg); return ret; } static int ipc_ksmbd_shutting_down(void) { return 0; } int ipc_process_event(void) { int ret = 0; ret = nl_recvmsgs_default(sk); if (ret < 0) { pr_err("Recv() error %s [%d]\n", nl_geterror(ret), ret); return -KSMBD_STATUS_IPC_FATAL_ERROR; } return ret; } static struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { [KSMBD_EVENT_UNSPEC] = { .minlen = 0, }, [KSMBD_EVENT_HEARTBEAT_REQUEST] = { .minlen = sizeof(struct ksmbd_heartbeat), }, [KSMBD_EVENT_STARTING_UP] = { .minlen = sizeof(struct ksmbd_startup_request), }, [KSMBD_EVENT_SHUTTING_DOWN] = { .minlen = sizeof(struct ksmbd_shutdown_request), }, [KSMBD_EVENT_LOGIN_REQUEST] = { .minlen = sizeof(struct ksmbd_login_request), }, [KSMBD_EVENT_LOGIN_RESPONSE] = { .minlen = sizeof(struct ksmbd_login_response), }, [KSMBD_EVENT_SHARE_CONFIG_REQUEST] = { .minlen = sizeof(struct ksmbd_share_config_request), }, [KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = { .minlen = sizeof(struct ksmbd_share_config_response), }, [KSMBD_EVENT_TREE_CONNECT_REQUEST] = { .minlen = sizeof(struct ksmbd_tree_connect_request), }, [KSMBD_EVENT_TREE_CONNECT_RESPONSE] = { .minlen = sizeof(struct ksmbd_tree_connect_response), }, [KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = { .minlen = sizeof(struct ksmbd_tree_disconnect_request), }, [KSMBD_EVENT_LOGOUT_REQUEST] = { .minlen = sizeof(struct ksmbd_logout_request), }, [KSMBD_EVENT_RPC_REQUEST] = { .minlen = sizeof(struct ksmbd_rpc_command), }, [KSMBD_EVENT_RPC_RESPONSE] = { .minlen = sizeof(struct ksmbd_rpc_command), }, [KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = { .minlen = sizeof(struct ksmbd_spnego_authen_request), }, [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { .minlen = sizeof(struct ksmbd_spnego_authen_response), }, }; static struct genl_cmd ksmbd_genl_cmds[] = { { .c_id = KSMBD_EVENT_UNSPEC, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_unsupported_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_HEARTBEAT_REQUEST, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_generic_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_STARTING_UP, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_unsupported_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_SHUTTING_DOWN, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_unsupported_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_LOGIN_REQUEST, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_generic_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_LOGIN_RESPONSE, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_unsupported_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_SHARE_CONFIG_REQUEST, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_generic_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_SHARE_CONFIG_RESPONSE, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_unsupported_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_TREE_CONNECT_REQUEST, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_generic_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_TREE_CONNECT_RESPONSE, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_unsupported_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_TREE_DISCONNECT_REQUEST, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_generic_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_LOGOUT_REQUEST, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_generic_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_RPC_REQUEST, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_generic_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_RPC_RESPONSE, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_unsupported_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_generic_event, .c_maxattr = KSMBD_EVENT_MAX, }, { .c_id = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, .c_attr_policy = ksmbd_nl_policy, .c_msg_parser = &handle_unsupported_event, .c_maxattr = KSMBD_EVENT_MAX, }, }; static struct genl_ops ksmbd_family_ops = { .o_name = KSMBD_GENL_NAME, .o_cmds = ksmbd_genl_cmds, .o_ncmds = ARRAY_SIZE(ksmbd_genl_cmds), }; int ipc_msg_send(struct ksmbd_ipc_msg *msg) { struct nl_msg *nlmsg; struct nlmsghdr *hdr; int ret = -EINVAL; nlmsg = nlmsg_alloc(); if (!nlmsg) { ret = -ENOMEM; goto out_error; } nlmsg_set_proto(nlmsg, NETLINK_GENERIC); hdr = genlmsg_put(nlmsg, getpid(), 0, ksmbd_family_ops.o_id, 0, 0, msg->type, KSMBD_GENL_VERSION); if (!hdr) { pr_err("genlmsg_put() has failed, aborting IPC send()\n"); goto out_error; } /* Use msg->type as attribute TYPE */ ret = nla_put(nlmsg, msg->type, msg->sz, KSMBD_IPC_MSG_PAYLOAD(msg)); if (ret) { pr_err("nla_put() has failed, aborting IPC send()\n"); goto out_error; } #if TRACING_DUMP_NL_MSG nl_msg_dump(nlmsg, stdout); #endif nl_complete_msg(sk, nlmsg); ret = nl_send_auto(sk, nlmsg); if (ret > 0) ret = 0; else pr_err("nl_send_auto() has failed: %d\n", ret); out_error: if (nlmsg) nlmsg_free(nlmsg); return ret; } void ipc_destroy(void) { if (ksmbd_health_status & KSMBD_HEALTH_RUNNING) { ipc_ksmbd_shutting_down(); genl_unregister_family(&ksmbd_family_ops); } nl_socket_free(sk); sk = NULL; } int ipc_init(void) { int ret; sk = nl_socket_alloc(); if (!sk) { pr_err("Cannot allocate netlink socket\n"); goto out_error; } nl_socket_disable_seq_check(sk); if (nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, nlink_msg_cb, NULL)) goto out_error; if (nl_connect(sk, NETLINK_GENERIC)) { pr_err("Cannot connect to generic netlink.\n"); goto out_error; } if (genl_register_family(&ksmbd_family_ops)) { pr_err("Cannot register netlink family\n"); goto out_error; } do { /* * Chances are we can start before ksmbd kernel module is up * and running. So just wait for the kksmbd to register the * netlink family and accept our connection. */ ret = genl_ops_resolve(sk, &ksmbd_family_ops); if (ret) { pr_err("Cannot resolve netlink family\n"); sleep(5); } } while (ret); if (ipc_ksmbd_starting_up()) { pr_err("Unable to send startup event\n"); return -EINVAL; } ksmbd_health_status = KSMBD_HEALTH_RUNNING; return 0; out_error: ipc_destroy(); return -EINVAL; } ksmbd-tools-3.4.4/mountd/mountd.c000066400000000000000000000311261417226526000167500ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include "ipc.h" #include "rpc.h" #include "worker.h" #include "config_parser.h" #include "management/user.h" #include "management/share.h" #include "management/session.h" #include "management/tree_conn.h" #include "management/spnego.h" #include "version.h" static int no_detach = 0; int ksmbd_health_status; static pid_t worker_pid; static int lock_fd = -1; typedef int (*worker_fn)(void); static void usage(void) { fprintf(stderr, "Usage: ksmbd\n"); fprintf(stderr, "\t--p=NUM | --port=NUM TCP port NUM\n"); fprintf(stderr, "\t--c=smb.conf | --config=smb.conf config file\n"); fprintf(stderr, "\t--u=pwd.db | --users=pwd.db Users DB\n"); fprintf(stderr, "\t--n | --nodetach Don't detach\n"); fprintf(stderr, "\t--s | --systemd Service mode\n"); fprintf(stderr, "\t-V | --version Show version\n"); fprintf(stderr, "\t-h | --help Show help\n"); exit(EXIT_FAILURE); } static void show_version(void) { printf("ksmbd-tools version : %s\n", KSMBD_TOOLS_VERSION); exit(EXIT_FAILURE); } static int handle_orphaned_lock_file(void) { char proc_ent[64] = {0, }; char manager_pid[10] = {0, }; int pid = 0; int fd; fd = open(KSMBD_LOCK_FILE, O_RDONLY); if (fd < 0) return -EINVAL; if (read(fd, &manager_pid, sizeof(manager_pid)) == -1) { pr_debug("Unable to read main PID: %s\n", strerr(errno)); close(fd); return -EINVAL; } close(fd); pid = strtol(manager_pid, NULL, 10); snprintf(proc_ent, sizeof(proc_ent), "/proc/%d", pid); fd = open(proc_ent, O_RDONLY); if (fd < 0) { pr_info("Unlink orphaned '%s'\n", KSMBD_LOCK_FILE); return unlink(KSMBD_LOCK_FILE); } close(fd); pr_info("File '%s' belongs to pid %d\n", KSMBD_LOCK_FILE, pid); return -EINVAL; } static int create_lock_file(void) { char manager_pid[10]; size_t sz; retry: lock_fd = open(KSMBD_LOCK_FILE, O_CREAT | O_EXCL | O_WRONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); if (lock_fd < 0) { if (handle_orphaned_lock_file()) return -EINVAL; goto retry; } if (flock(lock_fd, LOCK_EX | LOCK_NB) != 0) return -EINVAL; sz = snprintf(manager_pid, sizeof(manager_pid), "%d", getpid()); if (write(lock_fd, manager_pid, sz) == -1) pr_err("Unable to record main PID: %s\n", strerr(errno)); return 0; } char *make_path_subauth(void) { char *path; int smbconf_len = strlen(global_conf.smbconf); int loc = 0; const char *subauth_filename = "ksmbd.subauth"; if (strchr(global_conf.smbconf, '/')) { for (loc = smbconf_len - 1; loc > 0; loc--) if (global_conf.smbconf[loc] == '/') { loc++; break; } } path = calloc(1, loc + strlen(subauth_filename) + 1); if (!path) return NULL; strncat(path, global_conf.smbconf, loc); strcat(path, subauth_filename); return path; } /* * Write to file safely; by using a tmp and atomic rename. * Avoids a corrupt file if the write would be interrupted due * to a power failure. */ static int write_file_safe(char *path, char *buff, size_t length, int mode) { int fd, ret = -1; char *path_tmp = g_strdup_printf("%s.tmp", path); if (g_file_test(path_tmp, G_FILE_TEST_EXISTS)) unlink(path_tmp); fd = open(path_tmp, O_CREAT | O_EXCL | O_WRONLY, mode); if (fd < 0) { pr_err("Unable to create %s: %s\n", path_tmp, strerr(errno)); goto err_out; } if (write(fd, buff, length) == -1) { pr_err("Unable to write to %s: %s\n", path_tmp, strerr(errno)); close(fd); goto err_out; } fsync(fd); close(fd); if (rename(path_tmp, path)) { pr_err("Unable to rename to %s: %s\n", path, strerr(errno)); goto err_out; } ret = 0; err_out: g_free(path_tmp); return ret; } static int create_subauth_file(char *path_subauth) { char subauth_buf[35]; GRand *rnd; rnd = g_rand_new(); sprintf(subauth_buf, "%d:%d:%d\n", g_rand_int_range(rnd, 0, INT_MAX), g_rand_int_range(rnd, 0, INT_MAX), g_rand_int_range(rnd, 0, INT_MAX)); return write_file_safe(path_subauth, subauth_buf, strlen(subauth_buf), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); } static int generate_sub_auth(void) { int rc = -EINVAL; char *path_subauth; path_subauth = make_path_subauth(); if (!path_subauth) return -ENOMEM; retry: if (g_file_test(path_subauth, G_FILE_TEST_EXISTS)) { rc = cp_parse_subauth(path_subauth); } if (rc < 0) { rc = create_subauth_file(path_subauth); if (rc) { free(path_subauth); return -1; } goto retry; } free(path_subauth); return 0; } static void delete_lock_file(void) { if (lock_fd == -1) return; flock(lock_fd, LOCK_UN); close(lock_fd); lock_fd = -1; remove(KSMBD_LOCK_FILE); } static int wait_group_kill(int signo) { pid_t pid; int status; if (kill(worker_pid, signo) != 0) pr_err("can't execute kill %d: %s\n", worker_pid, strerr(errno)); while (1) { pid = waitpid(-1, &status, 0); if (pid != 0) { pr_debug("detected pid %d termination\n", pid); break; } sleep(1); } return 0; } static int setup_signal_handler(int signo, sighandler_t handler) { int status; sigset_t full_set; struct sigaction act = {}; sigfillset(&full_set); act.sa_handler = handler; act.sa_mask = full_set; status = sigaction(signo, &act, NULL); if (status != 0) pr_err("Unable to register %s signal handler: %s", strsignal(signo), strerr(errno)); return status; } static int setup_signals(sighandler_t handler) { if (setup_signal_handler(SIGINT, handler) != 0) return -EINVAL; if (setup_signal_handler(SIGTERM, handler) != 0) return -EINVAL; if (setup_signal_handler(SIGABRT, handler) != 0) return -EINVAL; if (setup_signal_handler(SIGQUIT, handler) != 0) return -EINVAL; if (setup_signal_handler(SIGHUP, handler) != 0) return -EINVAL; return 0; } static int parse_configs(char *pwddb, char *smbconf) { int ret; ret = cp_parse_pwddb(pwddb); if (ret == -ENOENT) { pr_err("User database file does not exist. %s\n", "Only guest sessions (if permitted) will work."); } else if (ret) { pr_err("Unable to parse user database\n"); return ret; } ret = cp_parse_smbconf(smbconf); if (ret) { pr_err("Unable to parse smb configuration file\n"); return ret; } return 0; } static void worker_process_free(void) { /* * NOTE, this is the final release, we don't look at ref_count * values. User management should be destroyed last. */ spnego_destroy(); ipc_destroy(); rpc_destroy(); wp_destroy(); sm_destroy(); shm_destroy(); usm_destroy(); } static void child_sig_handler(int signo) { static volatile int fatal_delivered = 0; if (signo == SIGHUP) { /* * This is a signal handler, we can't take any locks, set * a flag and wait for normal execution context to re-read * the configs. */ ksmbd_health_status |= KSMBD_SHOULD_RELOAD_CONFIG; pr_debug("Scheduled a config reload action.\n"); return; } pr_info("Child received signal: %d (%s)\n", signo, strsignal(signo)); if (!g_atomic_int_compare_and_exchange(&fatal_delivered, 0, 1)) return; ksmbd_health_status &= ~KSMBD_HEALTH_RUNNING; worker_process_free(); exit(EXIT_SUCCESS); } static void manager_sig_handler(int signo) { /* * Pass SIGHUP to worker, so it will reload configs */ if (signo == SIGHUP) { if (!worker_pid) return; ksmbd_health_status |= KSMBD_SHOULD_RELOAD_CONFIG; if (kill(worker_pid, signo)) pr_err("Unable to send SIGHUP to %d: %s\n", worker_pid, strerr(errno)); return; } setup_signals(SIG_DFL); wait_group_kill(signo); pr_info("Exiting. Bye!\n"); delete_lock_file(); kill(0, SIGINT); } static int worker_process_init(void) { int ret; setup_signals(child_sig_handler); set_logger_app_name("ksmbd-worker"); ret = usm_init(); if (ret) { pr_err("Failed to init user management\n"); goto out; } ret = shm_init(); if (ret) { pr_err("Failed to init net share management\n"); goto out; } ret = parse_configs(global_conf.pwddb, global_conf.smbconf); if (ret) { pr_err("Failed to parse configuration files\n"); goto out; } ret = sm_init(); if (ret) { pr_err("Failed to init user session management\n"); goto out; } ret = wp_init(); if (ret) { pr_err("Failed to init worker threads pool\n"); goto out; } ret = rpc_init(); if (ret) { pr_err("Failed to init RPC subsystem\n"); goto out; } ret = ipc_init(); if (ret) { pr_err("Failed to init IPC subsystem\n"); goto out; } ret = spnego_init(); if (ret) { pr_err("Failed to init spnego subsystem\n"); ret = KSMBD_STATUS_IPC_FATAL_ERROR; goto out; } while (ksmbd_health_status & KSMBD_HEALTH_RUNNING) { ret = ipc_process_event(); if (ret == -KSMBD_STATUS_IPC_FATAL_ERROR) { ret = KSMBD_STATUS_IPC_FATAL_ERROR; break; } } out: worker_process_free(); return ret; } static pid_t start_worker_process(worker_fn fn) { int status = 0; pid_t __pid; __pid = fork(); if (__pid < 0) { pr_err("Can't fork child process: `%s'\n", strerr(errno)); return -EINVAL; } if (__pid == 0) { status = fn(); exit(status); } return __pid; } static int manager_process_init(void) { /* * Do not chdir() daemon()'d process to '/'. */ int nochdir = 1; setup_signals(manager_sig_handler); if (no_detach == 0) { pr_logger_init(PR_LOGGER_SYSLOG); if (daemon(nochdir, 0) != 0) { pr_err("Daemonization failed\n"); goto out; } } else { /* * Make ourselves a process group leader; if we are * the group leader already then the function will do * nothing (apart from setting errnor to EPERM). */ if (no_detach == 1) setsid(); } if (create_lock_file()) { pr_err("Failed to create lock file: %s\n", strerr(errno)); goto out; } if (generate_sub_auth()) pr_debug("Failed to generate subauth for domain sid: %s\n", strerr(errno)); worker_pid = start_worker_process(worker_process_init); if (worker_pid < 0) goto out; while (1) { int status; pid_t child; child = waitpid(-1, &status, 0); if (ksmbd_health_status & KSMBD_SHOULD_RELOAD_CONFIG && errno == EINTR) { ksmbd_health_status &= ~KSMBD_SHOULD_RELOAD_CONFIG; continue; } pr_err("WARNING: child process exited abnormally: %d\n", child); if (child == -1) { pr_err("waitpid() returned error code: %s\n", strerr(errno)); goto out; } if (WIFEXITED(status) && WEXITSTATUS(status) == KSMBD_STATUS_IPC_FATAL_ERROR) { pr_err("Fatal IPC error. Terminating. Check dmesg.\n"); goto out; } /* Ratelimit automatic restarts */ sleep(1); worker_pid = start_worker_process(worker_process_init); if (worker_pid < 0) goto out; } out: delete_lock_file(); kill(0, SIGTERM); return 0; } static int manager_systemd_service(void) { pid_t __pid; __pid = start_worker_process(manager_process_init); if (__pid < 0) return -EINVAL; return 0; } static struct option opts[] = { {"port", required_argument, NULL, 'p' }, {"config", required_argument, NULL, 'c' }, {"users", required_argument, NULL, 'u' }, {"systemd", no_argument, NULL, 's' }, {"nodetach", optional_argument, NULL, 'n' }, {"help", no_argument, NULL, 'h' }, {"?", no_argument, NULL, '?' }, {"version", no_argument, NULL, 'V' }, {NULL, 0, NULL, 0 } }; int main(int argc, char *argv[]) { int systemd_service = 0; int c; set_logger_app_name("ksmbd.mountd"); memset(&global_conf, 0x00, sizeof(struct smbconf_global)); global_conf.pwddb = PATH_PWDDB; global_conf.smbconf = PATH_SMBCONF; pr_logger_init(PR_LOGGER_STDIO); opterr = 0; while (1) { c = getopt_long(argc, argv, "n::p:c:u:sVh", opts, NULL); if (c < 0) break; switch (c) { case 0: /* getopt_long() set a variable, just keep going */ break; case 1: break; case 'p': global_conf.tcp_port = cp_get_group_kv_long(optarg); pr_debug("TCP port option override\n"); break; case 'c': global_conf.smbconf = g_strdup(optarg); break; case 'u': global_conf.pwddb = g_strdup(optarg); break; case 'n': if (!optarg) no_detach = 1; else no_detach = cp_get_group_kv_long(optarg); break; case 's': systemd_service = 1; break; case 'V': show_version(); break; case ':': pr_err("Missing option argument\n"); /* Fall through */ case '?': case 'h': /* Fall through */ default: usage(); } } if (!global_conf.smbconf || !global_conf.pwddb) { pr_err("Out of memory\n"); exit(EXIT_FAILURE); } setup_signals(manager_sig_handler); if (!systemd_service) return manager_process_init(); return manager_systemd_service(); } ksmbd-tools-3.4.4/mountd/rpc.c000066400000000000000000000720251417226526000162310ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #include #include static GHashTable *pipes_table; static GRWLock pipes_table_lock; /* * Version 2.0 data representation protocol * * UUID: 8a885d04-1ceb-11c9-9fe8-08002b104860 * VERSION: 2 * * * Transfer Syntax: Bind Time Feature Negotiation * UUID:6cb71c2c-9812-4540-0300-000000000000 * * 6CB71C2C-9812-4540 * * MUST BE BLOCKED * Interface: SRVSVC UUID: 4b324fc8-1670-01d3-1278-5a47bf6ee188 */ struct dcerpc_syntax_table { struct dcerpc_syntax syn; int ack_result; }; static struct dcerpc_syntax_table known_syntaxes[] = { { .syn.uuid.time_low = 0x8a885d04, .syn.uuid.time_mid = 0x1ceb, .syn.uuid.time_hi_and_version = 0x11c9, .syn.uuid.clock_seq = {0x9f, 0xe8}, .syn.uuid.node = {0x8, 0x0, 0x2b, 0x10, 0x48, 0x60}, .syn.ver_major = 0x2, .syn.ver_minor = 0x0, .ack_result = DCERPC_BIND_ACK_RES_ACCEPT, }, { .syn.uuid.time_low = 0x6CB71C2C, .syn.uuid.time_mid = 0x9812, .syn.uuid.time_hi_and_version = 0x4540, .syn.uuid.clock_seq = {0x0, 0x0}, .syn.uuid.node = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, .syn.ver_major = 0x1, .syn.ver_minor = 0x0, .ack_result = DCERPC_BIND_ACK_RES_NEGOTIATE_ACK, }, }; /* * PNIO uuid * Transfer Syntax: PNIO (Implicit Ar) * * All zero-s. */ static struct dcerpc_syntax negotiate_ack_PNIO_uuid; /* * We need a proper DCE RPC (ndr/ndr64) parser. And we also need a proper * IDL support... * Maybe someone smart and cool enough can do it for us. The one you can * find here is just a very simple implementation, which sort of works for * us, but we do realize that it sucks. * * Documentation: * * http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagfcjh_39 * https://msdn.microsoft.com/en-us/library/cc243858.aspx */ #define PAYLOAD_HEAD(d) ((d)->payload + (d)->offset) #define __ALIGN(x, a) \ ({ \ typeof(x) ret = (x); \ if (((x) & ((typeof(x))(a) - 1)) != 0) \ ret = __ALIGN_MASK(x, (typeof(x))(a) - 1); \ ret; \ }) #define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) static struct ksmbd_rpc_pipe *rpc_pipe_lookup(unsigned int id) { struct ksmbd_rpc_pipe *pipe; g_rw_lock_reader_lock(&pipes_table_lock); pipe = g_hash_table_lookup(pipes_table, &id); g_rw_lock_reader_unlock(&pipes_table_lock); return pipe; } static void dcerpc_free(struct ksmbd_dcerpc *dce) { if (!(dce->flags & KSMBD_DCERPC_EXTERNAL_PAYLOAD)) free(dce->payload); free(dce); } static struct ksmbd_dcerpc *dcerpc_alloc(unsigned int flags, int sz) { struct ksmbd_dcerpc *dce; dce = calloc(1, sizeof(struct ksmbd_dcerpc)); if (!dce) return NULL; dce->payload = calloc(1, sz); if (!dce->payload) { free(dce); return NULL; } dce->payload_sz = sz; dce->flags = flags; dce->num_pointers = 1; if (sz == KSMBD_DCERPC_MAX_PREFERRED_SIZE) dce->flags &= ~KSMBD_DCERPC_FIXED_PAYLOAD_SZ; return dce; } static struct ksmbd_dcerpc *dcerpc_ext_alloc(unsigned int flags, void *payload, int payload_sz) { struct ksmbd_dcerpc *dce; dce = calloc(1, sizeof(struct ksmbd_dcerpc)); if (!dce) return NULL; dce->payload = payload; dce->payload_sz = payload_sz; dce->flags = flags; dce->flags |= KSMBD_DCERPC_EXTERNAL_PAYLOAD; dce->flags |= KSMBD_DCERPC_FIXED_PAYLOAD_SZ; return dce; } void dcerpc_set_ext_payload(struct ksmbd_dcerpc *dce, void *payload, size_t sz) { dce->num_pointers = 1; dce->payload = payload; dce->payload_sz = sz; dce->offset = 0; dce->flags |= KSMBD_DCERPC_EXTERNAL_PAYLOAD; dce->flags |= KSMBD_DCERPC_FIXED_PAYLOAD_SZ; } void rpc_pipe_reset(struct ksmbd_rpc_pipe *pipe) { if (pipe->entry_processed) { while (pipe->num_entries) pipe->entry_processed(pipe, 0); } pipe->num_entries = 0; } static void __rpc_pipe_free(struct ksmbd_rpc_pipe *pipe) { rpc_pipe_reset(pipe); if (pipe->dce) dcerpc_free(pipe->dce); g_array_free(pipe->entries, 1); free(pipe); } static void rpc_pipe_free(struct ksmbd_rpc_pipe *pipe) { if (pipe->id != (unsigned int)-1) { g_rw_lock_writer_lock(&pipes_table_lock); g_hash_table_remove(pipes_table, &(pipe->id)); g_rw_lock_writer_unlock(&pipes_table_lock); } __rpc_pipe_free(pipe); } static struct ksmbd_rpc_pipe *rpc_pipe_alloc(void) { struct ksmbd_rpc_pipe *pipe; pipe = calloc(1, sizeof(struct ksmbd_rpc_pipe)); if (!pipe) return NULL; pipe->id = -1; pipe->entries = g_array_new(0, 0, sizeof(void *)); if (!pipe->entries) { rpc_pipe_free(pipe); return NULL; } return pipe; } static struct ksmbd_rpc_pipe *rpc_pipe_alloc_bind(unsigned int id) { struct ksmbd_rpc_pipe *pipe = rpc_pipe_alloc(); int ret; if (!pipe) return NULL; pipe->id = id; g_rw_lock_writer_lock(&pipes_table_lock); ret = g_hash_table_insert(pipes_table, &(pipe->id), pipe); g_rw_lock_writer_unlock(&pipes_table_lock); if (!ret) { pipe->id = (unsigned int)-1; rpc_pipe_free(pipe); pipe = NULL; } return pipe; } static void free_hash_entry(gpointer k, gpointer s, gpointer user_data) { __rpc_pipe_free(s); } static void __clear_pipes_table(void) { g_rw_lock_writer_lock(&pipes_table_lock); g_hash_table_foreach(pipes_table, free_hash_entry, NULL); g_rw_lock_writer_unlock(&pipes_table_lock); } static void align_offset(struct ksmbd_dcerpc *dce, size_t n) { dce->offset = __ALIGN(dce->offset, n); } void auto_align_offset(struct ksmbd_dcerpc *dce) { if (dce->flags & KSMBD_DCERPC_ALIGN8) dce->offset = __ALIGN(dce->offset, 8); else if (dce->flags & KSMBD_DCERPC_ALIGN4) dce->offset = __ALIGN(dce->offset, 4); } static int try_realloc_payload(struct ksmbd_dcerpc *dce, size_t data_sz) { char *n; if (dce->offset + data_sz < dce->payload_sz) return 0; if (dce->flags & KSMBD_DCERPC_FIXED_PAYLOAD_SZ) { pr_err("DCE RPC: fixed payload buffer overflow\n"); return -ENOMEM; } n = realloc(dce->payload, dce->payload_sz + 4096); if (!n) return -ENOMEM; dce->payload = n; dce->payload_sz += 4096; memset(dce->payload + dce->offset, 0, dce->payload_sz - dce->offset); return 0; } static __u8 noop_int8(__u8 v) { return v; } #define htobe_n noop_int8 #define htole_n noop_int8 #define betoh_n noop_int8 #define letoh_n noop_int8 #define NDR_WRITE_INT(name, type, be, le) \ int ndr_write_##name(struct ksmbd_dcerpc *dce, type value) \ { \ if (try_realloc_payload(dce, sizeof(value))) \ return -ENOMEM; \ align_offset(dce, sizeof(type)); \ if (dce->flags & KSMBD_DCERPC_LITTLE_ENDIAN) \ *(type *)PAYLOAD_HEAD(dce) = le(value); \ else \ *(type *)PAYLOAD_HEAD(dce) = be(value); \ dce->offset += sizeof(value); \ return 0; \ } NDR_WRITE_INT(int8, __u8, htobe_n, htole_n); NDR_WRITE_INT(int16, __u16, htobe16, htole16); NDR_WRITE_INT(int32, __u32, htobe32, htole32); NDR_WRITE_INT(int64, __u64, htobe64, htole64); #define NDR_READ_INT(name, type, be, le) \ type ndr_read_##name(struct ksmbd_dcerpc *dce) \ { \ type ret; \ \ align_offset(dce, sizeof(type)); \ if (dce->flags & KSMBD_DCERPC_LITTLE_ENDIAN) \ ret = le(*(type *)PAYLOAD_HEAD(dce)); \ else \ ret = be(*(type *)PAYLOAD_HEAD(dce)); \ dce->offset += sizeof(type); \ return ret; \ } NDR_READ_INT(int8, __u8, betoh_n, letoh_n); NDR_READ_INT(int16, __u16, be16toh, le16toh); NDR_READ_INT(int32, __u32, be32toh, le32toh); NDR_READ_INT(int64, __u64, be64toh, le64toh); /* * For a non-encapsulated union, the discriminant is marshalled into * the transmitted data stream twice: once as the field or parameter, * which is referenced by the switch_is construct, in the procedure * argument list; and once as the first part of the union * representation. */ #define NDR_WRITE_UNION(name, type) \ int ndr_write_union_##name(struct ksmbd_dcerpc *dce, type value) \ { \ int ret; \ \ ret = ndr_write_##name(dce, value); \ ret |= ndr_write_##name(dce, value); \ return ret; \ } NDR_WRITE_UNION(int16, __u16); NDR_WRITE_UNION(int32, __u32); #define NDR_READ_UNION(name, type) \ type ndr_read_union_##name(struct ksmbd_dcerpc *dce) \ { \ type ret = ndr_read_##name(dce); \ if (ndr_read_##name(dce) != ret) { \ pr_err("NDR: union representation mismatch %lu\n", \ (unsigned long)ret); \ ret = -EINVAL; \ } \ return ret; \ } NDR_READ_UNION(int32, __u32); int ndr_write_bytes(struct ksmbd_dcerpc *dce, void *value, size_t sz) { if (try_realloc_payload(dce, sizeof(short))) return -ENOMEM; align_offset(dce, 2); memcpy(PAYLOAD_HEAD(dce), value, sz); dce->offset += sz; return 0; } int ndr_read_bytes(struct ksmbd_dcerpc *dce, void *value, size_t sz) { align_offset(dce, 2); memcpy(value, PAYLOAD_HEAD(dce), sz); dce->offset += sz; return 0; } static gchar *ndr_convert_char_to_unicode(struct ksmbd_dcerpc *dce, char *str, size_t len, gsize *bytes_written) { gchar *out; gsize bytes_read = 0; int charset = KSMBD_CHARSET_UTF16LE; if (!(dce->flags & KSMBD_DCERPC_LITTLE_ENDIAN)) charset = KSMBD_CHARSET_UTF16BE; if (dce->flags & KSMBD_DCERPC_ASCII_STRING) charset = KSMBD_CHARSET_UTF8; out = ksmbd_gconvert(str, len, charset, KSMBD_CHARSET_DEFAULT, &bytes_read, bytes_written); return out; } int ndr_write_vstring(struct ksmbd_dcerpc *dce, void *value) { gchar *out; gsize bytes_written = 0; size_t raw_len, str_len; char *raw_value = value; int ret; if (!value) raw_value = ""; raw_len = strlen(raw_value) + 1; out = ndr_convert_char_to_unicode(dce, raw_value, raw_len, &bytes_written); if (!out) return -EINVAL; str_len = g_utf8_strlen(raw_value, -1) + 1; /* * NDR represents a conformant and varying string as an ordered * sequence of representations of the string elements, preceded * by three unsigned long integers. The first integer gives the * maximum number of elements in the string, including the terminator. * The second integer gives the offset from the first index of the * string to the first index of the actual subset being passed. * The third integer gives the actual number of elements being * passed, including the terminator. */ ret = ndr_write_int32(dce, str_len); ret |= ndr_write_int32(dce, 0); ret |= ndr_write_int32(dce, str_len); ret |= ndr_write_bytes(dce, out, bytes_written); auto_align_offset(dce); g_free(out); return ret; } int ndr_write_string(struct ksmbd_dcerpc *dce, char *str) { gchar *out; gsize bytes_written = 0; size_t len; int ret; if (!str) str = ""; len = strlen(str); out = ndr_convert_char_to_unicode(dce, str, len, &bytes_written); if (!out) return -EINVAL; ret = ndr_write_int32(dce, len); // max count ret |= ndr_write_int32(dce, 0); ret |= ndr_write_int32(dce, len); // actual count ret |= ndr_write_bytes(dce, out, bytes_written); auto_align_offset(dce); g_free(out); return ret; } int ndr_write_lsa_string(struct ksmbd_dcerpc *dce, char *str) { gchar *out; gsize bytes_written = 0; size_t len; int ret; if (!str) str = ""; len = strlen(str); out = ndr_convert_char_to_unicode(dce, str, len, &bytes_written); if (!out) return -EINVAL; ret = ndr_write_int32(dce, len + 1); // max count ret |= ndr_write_int32(dce, 0); ret |= ndr_write_int32(dce, len); // actual count ret |= ndr_write_bytes(dce, out, bytes_written); auto_align_offset(dce); g_free(out); return ret; } char *ndr_read_vstring(struct ksmbd_dcerpc *dce) { gchar *out; gsize bytes_read = 0; gsize bytes_written = 0; size_t raw_len; int charset = KSMBD_CHARSET_UTF16LE; raw_len = ndr_read_int32(dce); ndr_read_int32(dce); /* read in offset */ ndr_read_int32(dce); if (!(dce->flags & KSMBD_DCERPC_LITTLE_ENDIAN)) charset = KSMBD_CHARSET_UTF16BE; if (dce->flags & KSMBD_DCERPC_ASCII_STRING) charset = KSMBD_CHARSET_UTF8; if (raw_len == 0) { out = strdup(""); return out; } out = ksmbd_gconvert(PAYLOAD_HEAD(dce), raw_len * 2, KSMBD_CHARSET_DEFAULT, charset, &bytes_read, &bytes_written); if (!out) return NULL; dce->offset += raw_len * 2; auto_align_offset(dce); return out; } void ndr_read_vstring_ptr(struct ksmbd_dcerpc *dce, struct ndr_char_ptr *ctr) { ctr->ptr = ndr_read_vstring(dce); } void ndr_read_uniq_vsting_ptr(struct ksmbd_dcerpc *dce, struct ndr_uniq_char_ptr *ctr) { ctr->ref_id = ndr_read_int32(dce); if (ctr->ref_id == 0) { ctr->ptr = 0; return; } ctr->ptr = ndr_read_vstring(dce); } void ndr_free_vstring_ptr(struct ndr_char_ptr *ctr) { free(ctr->ptr); ctr->ptr = NULL; } void ndr_free_uniq_vsting_ptr(struct ndr_uniq_char_ptr *ctr) { ctr->ref_id = 0; free(ctr->ptr); ctr->ptr = NULL; } void ndr_read_ptr(struct ksmbd_dcerpc *dce, struct ndr_ptr *ctr) { ctr->ptr = ndr_read_int32(dce); } void ndr_read_uniq_ptr(struct ksmbd_dcerpc *dce, struct ndr_uniq_ptr *ctr) { ctr->ref_id = ndr_read_int32(dce); if (ctr->ref_id == 0) { ctr->ptr = 0; return; } ctr->ptr = ndr_read_int32(dce); } static int __max_entries(struct ksmbd_dcerpc *dce, struct ksmbd_rpc_pipe *pipe) { int current_size, i; if (!(dce->flags & KSMBD_DCERPC_FIXED_PAYLOAD_SZ)) return pipe->num_entries; if (!dce->entry_size) { pr_err("No ->entry_size() callback was provided\n"); return pipe->num_entries; } current_size = 0; for (i = 0; i < pipe->num_entries; i++) { gpointer entry; entry = g_array_index(pipe->entries, gpointer, i); current_size += dce->entry_size(dce, entry); if (current_size < 4 * dce->payload_sz / 5) continue; return i; } return pipe->num_entries; } int __ndr_write_array_of_structs(struct ksmbd_rpc_pipe *pipe, int max_entry_nr) { struct ksmbd_dcerpc *dce = pipe->dce; int i; for (i = 0; i < max_entry_nr; i++) { gpointer entry; entry = g_array_index(pipe->entries, gpointer, i); if (dce->entry_rep(dce, entry)) return KSMBD_RPC_EBAD_DATA; } for (i = 0; i < max_entry_nr; i++) { gpointer entry; entry = g_array_index(pipe->entries, gpointer, i); if (dce->entry_data(dce, entry)) return KSMBD_RPC_EBAD_DATA; } if (pipe->entry_processed) { for (i = 0; i < max_entry_nr; i++) pipe->entry_processed(pipe, 0); } return KSMBD_RPC_OK; } static int ndr_write_empty_array_of_struct(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); ndr_write_int32(dce, 0); ndr_write_int32(dce, 0); return KSMBD_RPC_OK; } int ndr_write_array_of_structs(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int max_entry_nr; int ret = KSMBD_RPC_OK; /* * In the NDR representation of a structure that contains a * conformant and varying array, the maximum counts for dimensions * of the array are moved to the beginning of the structure, but * the offsets and actual counts remain in place at the end of the * structure, immediately preceding the array elements. */ if (pipe->num_entries == 0) return ndr_write_empty_array_of_struct(pipe); max_entry_nr = __max_entries(dce, pipe); if (max_entry_nr != pipe->num_entries) ret = KSMBD_RPC_EMORE_DATA; ndr_write_int32(dce, max_entry_nr); /* * ARRAY representation [per dimension] * max_count * offset * actual_count * element representation [1..N] * actual elements [1..N] */ ndr_write_int32(dce, max_entry_nr); ndr_write_int32(dce, 1); ndr_write_int32(dce, max_entry_nr); if (max_entry_nr == 0) { pr_err("DCERPC: can't fit any data, buffer is too small\n"); rpc_pipe_reset(pipe); return KSMBD_RPC_EBAD_DATA; } return __ndr_write_array_of_structs(pipe, max_entry_nr); } int rpc_init(void) { pipes_table = g_hash_table_new(g_int_hash, g_int_equal); if (!pipes_table) return -ENOMEM; g_rw_lock_init(&pipes_table_lock); rpc_samr_init(); rpc_lsarpc_init(); return 0; } void rpc_destroy(void) { if (pipes_table) { __clear_pipes_table(); g_hash_table_destroy(pipes_table); } g_rw_lock_clear(&pipes_table_lock); rpc_samr_destroy(); rpc_lsarpc_destroy(); } static int dcerpc_hdr_write(struct ksmbd_dcerpc *dce, struct dcerpc_header *hdr) { ndr_write_int8(dce, hdr->rpc_vers); ndr_write_int8(dce, hdr->rpc_vers_minor); ndr_write_int8(dce, hdr->ptype); ndr_write_int8(dce, hdr->pfc_flags); ndr_write_bytes(dce, &hdr->packed_drep, sizeof(hdr->packed_drep)); ndr_write_int16(dce, hdr->frag_length); ndr_write_int16(dce, hdr->auth_length); ndr_write_int32(dce, hdr->call_id); return 0; } static int dcerpc_hdr_read(struct ksmbd_dcerpc *dce, struct dcerpc_header *hdr) { /* Common Type Header for the Serialization Stream */ hdr->rpc_vers = ndr_read_int8(dce); hdr->rpc_vers_minor = ndr_read_int8(dce); hdr->ptype = ndr_read_int8(dce); hdr->pfc_flags = ndr_read_int8(dce); /* * This common type header MUST be presented by using * little-endian format in the octet stream. The first * byte of the common type header MUST be equal to 1 to * indicate level 1 of type serialization. * * Type serialization version 1 can use either a little-endian * or big-endian integer and floating-pointer byte order but * MUST use the IEEE floating-point format representation and * ASCII character format. */ ndr_read_bytes(dce, &hdr->packed_drep, sizeof(hdr->packed_drep)); dce->flags |= KSMBD_DCERPC_ALIGN4; dce->flags |= KSMBD_DCERPC_LITTLE_ENDIAN; if (hdr->packed_drep[0] != DCERPC_SERIALIZATION_LITTLE_ENDIAN) dce->flags &= ~KSMBD_DCERPC_LITTLE_ENDIAN; hdr->frag_length = ndr_read_int16(dce); hdr->auth_length = ndr_read_int16(dce); hdr->call_id = ndr_read_int32(dce); return 0; } static int dcerpc_response_hdr_write(struct ksmbd_dcerpc *dce, struct dcerpc_response_header *hdr) { ndr_write_int32(dce, hdr->alloc_hint); ndr_write_int16(dce, hdr->context_id); ndr_write_int8(dce, hdr->cancel_count); auto_align_offset(dce); return 0; } static int dcerpc_request_hdr_read(struct ksmbd_dcerpc *dce, struct dcerpc_request_header *hdr) { hdr->alloc_hint = ndr_read_int32(dce); hdr->context_id = ndr_read_int16(dce); hdr->opnum = ndr_read_int16(dce); return 0; } int dcerpc_write_headers(struct ksmbd_dcerpc *dce, int method_status) { int payload_offset; payload_offset = dce->offset; dce->offset = 0; dce->hdr.ptype = DCERPC_PTYPE_RPC_RESPONSE; dce->hdr.pfc_flags = DCERPC_PFC_FIRST_FRAG | DCERPC_PFC_LAST_FRAG; dce->hdr.frag_length = payload_offset; if (method_status == KSMBD_RPC_EMORE_DATA) dce->hdr.pfc_flags = 0; dcerpc_hdr_write(dce, &dce->hdr); /* cast req_hdr to resp_hdr and NULL out lower 2 bytes */ dce->req_hdr.opnum = 0; dce->resp_hdr.cancel_count = 0; dce->resp_hdr.alloc_hint = payload_offset; dcerpc_response_hdr_write(dce, &dce->resp_hdr); dce->offset = payload_offset; return 0; } static int __dcerpc_read_syntax(struct ksmbd_dcerpc *dce, struct dcerpc_syntax *syn) { syn->uuid.time_low = ndr_read_int32(dce); syn->uuid.time_mid = ndr_read_int16(dce); syn->uuid.time_hi_and_version = ndr_read_int16(dce); ndr_read_bytes(dce, syn->uuid.clock_seq, sizeof(syn->uuid.clock_seq)); ndr_read_bytes(dce, syn->uuid.node, sizeof(syn->uuid.node)); syn->ver_major = ndr_read_int16(dce); syn->ver_minor = ndr_read_int16(dce); return 0; } static int __dcerpc_write_syntax(struct ksmbd_dcerpc *dce, struct dcerpc_syntax *syn) { ndr_write_int32(dce, syn->uuid.time_low); ndr_write_int16(dce, syn->uuid.time_mid); ndr_write_int16(dce, syn->uuid.time_hi_and_version); ndr_write_bytes(dce, syn->uuid.clock_seq, sizeof(syn->uuid.clock_seq)); ndr_write_bytes(dce, syn->uuid.node, sizeof(syn->uuid.node)); ndr_write_int16(dce, syn->ver_major); ndr_write_int16(dce, syn->ver_minor); return 0; } static void dcerpc_bind_req_free(struct dcerpc_bind_request *hdr) { int i; for (i = 0; i < hdr->num_contexts; i++) free(hdr->list[i].transfer_syntaxes); free(hdr->list); hdr->list = NULL; hdr->num_contexts = 0; } static int dcerpc_parse_bind_req(struct ksmbd_dcerpc *dce, struct dcerpc_bind_request *hdr) { int i, j; hdr->flags = dce->rpc_req->flags; hdr->max_xmit_frag_sz = ndr_read_int16(dce); hdr->max_recv_frag_sz = ndr_read_int16(dce); hdr->assoc_group_id = ndr_read_int32(dce); hdr->list = NULL; hdr->num_contexts = ndr_read_int8(dce); auto_align_offset(dce); if (!hdr->num_contexts) return 0; hdr->list = calloc(hdr->num_contexts, sizeof(struct dcerpc_context)); if (!hdr->list) return -ENOMEM; for (i = 0; i < hdr->num_contexts; i++) { struct dcerpc_context *ctx = &hdr->list[i]; ctx->id = ndr_read_int16(dce); ctx->num_syntaxes = ndr_read_int8(dce); if (!ctx->num_syntaxes) { pr_err("BIND: zero syntaxes provided\n"); return -EINVAL; } __dcerpc_read_syntax(dce, &ctx->abstract_syntax); ctx->transfer_syntaxes = calloc(ctx->num_syntaxes, sizeof(struct dcerpc_syntax)); if (!ctx->transfer_syntaxes) return -ENOMEM; for (j = 0; j < ctx->num_syntaxes; j++) __dcerpc_read_syntax(dce, &ctx->transfer_syntaxes[j]); } return KSMBD_RPC_OK; } static int dcerpc_bind_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce; dce = pipe->dce; if (dcerpc_parse_bind_req(dce, &dce->bi_req)) return KSMBD_RPC_EBAD_DATA; pipe->entry_processed = NULL; return KSMBD_RPC_OK; } static int dcerpc_syntax_cmp(struct dcerpc_syntax *a, struct dcerpc_syntax *b) { if (a->uuid.time_low != b->uuid.time_low) return -1; if (a->uuid.time_mid != b->uuid.time_mid) return -1; if (a->uuid.time_hi_and_version != b->uuid.time_hi_and_version) return -1; if (a->ver_major != b->ver_major) return -1; return 0; } static int dcerpc_syntax_supported(struct dcerpc_syntax *a) { int k; for (k = 0; k < ARRAY_SIZE(known_syntaxes); k++) { struct dcerpc_syntax *b = &known_syntaxes[k].syn; if (!dcerpc_syntax_cmp(a, b)) return known_syntaxes[k].ack_result; } return -1; } static int dcerpc_bind_nack_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int i, payload_offset; dce->offset = sizeof(struct dcerpc_header); ndr_write_int16(dce, DCERPC_BIND_NAK_RSN_PROTOCOL_VERSION_NOT_SUPPORTED); ndr_write_int8(dce, ARRAY_SIZE(known_syntaxes)); auto_align_offset(dce); for (i = 0; i < ARRAY_SIZE(known_syntaxes); i++) { ndr_write_int8(dce, known_syntaxes[i].syn.ver_major); ndr_write_int8(dce, known_syntaxes[i].syn.ver_minor); } payload_offset = dce->offset; dce->offset = 0; dce->hdr.ptype = DCERPC_PTYPE_RPC_BINDNACK; dce->hdr.pfc_flags = DCERPC_PFC_FIRST_FRAG | DCERPC_PFC_LAST_FRAG; dce->hdr.frag_length = payload_offset; dcerpc_hdr_write(dce, &dce->hdr); dce->offset = payload_offset; dce->rpc_resp->payload_sz = dce->offset; return KSMBD_RPC_OK; } static int dcerpc_bind_ack_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int num_trans, i, payload_offset; char *addr; dce->offset = sizeof(struct dcerpc_header); /* * Preserve bind assoc group, if was specified. */ if (dce->bi_req.assoc_group_id == 0) dce->bi_req.assoc_group_id = 0x53f0; ndr_write_int16(dce, dce->bi_req.max_xmit_frag_sz); ndr_write_int16(dce, dce->bi_req.max_recv_frag_sz); ndr_write_int32(dce, dce->bi_req.assoc_group_id); if (dce->bi_req.flags & KSMBD_RPC_SRVSVC_METHOD_INVOKE) addr = "\\PIPE\\srvsvc"; else if (dce->bi_req.flags & KSMBD_RPC_WKSSVC_METHOD_INVOKE) addr = "\\PIPE\\wkssvc"; else if (dce->bi_req.flags & KSMBD_RPC_SAMR_METHOD_INVOKE) addr = "\\PIPE\\samr"; else if (dce->bi_req.flags & KSMBD_RPC_LSARPC_METHOD_INVOKE) addr = "\\PIPE\\lsarpc"; else return KSMBD_RPC_EBAD_FUNC; if (dce->hdr.ptype == DCERPC_PTYPE_RPC_ALTCONT) { ndr_write_int16(dce, 0); ndr_write_int16(dce, 0); } else { ndr_write_int16(dce, strlen(addr)); ndr_write_bytes(dce, addr, strlen(addr)); } align_offset(dce, 4); /* [flag(NDR_ALIGN4)] DATA_BLOB _pad1; */ num_trans = dce->bi_req.num_contexts; ndr_write_int8(dce, num_trans); align_offset(dce, 2); for (i = 0; i < num_trans; i++) { struct dcerpc_syntax *s; __s16 result; s = &dce->bi_req.list[i].transfer_syntaxes[0]; result = dcerpc_syntax_supported(s); if (result == -1) { result = DCERPC_BIND_ACK_RES_PROVIDER_REJECT; ndr_write_union_int16(dce, result); } else { if (result == DCERPC_BIND_ACK_RES_ACCEPT) ndr_write_union_int16(dce, result); if (result == DCERPC_BIND_ACK_RES_NEGOTIATE_ACK) { ndr_write_int16(dce, result); ndr_write_int16(dce, 0x00); s = &negotiate_ack_PNIO_uuid; } } __dcerpc_write_syntax(dce, s); } payload_offset = dce->offset; dce->offset = 0; if (dce->hdr.ptype == DCERPC_PTYPE_RPC_ALTCONT) dce->hdr.ptype = DCERPC_PTYPE_RPC_ALTCONTRESP; else dce->hdr.ptype = DCERPC_PTYPE_RPC_BINDACK; dce->hdr.pfc_flags = DCERPC_PFC_FIRST_FRAG | DCERPC_PFC_LAST_FRAG; dce->hdr.frag_length = payload_offset; dcerpc_hdr_write(dce, &dce->hdr); dce->offset = payload_offset; dce->rpc_resp->payload_sz = dce->offset; return KSMBD_RPC_OK; } static int dcerpc_bind_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int i, j, ack = 0, ret; for (i = 0; i < dce->bi_req.num_contexts; i++) { for (j = 0; j < dce->bi_req.list[i].num_syntaxes; j++) { static struct dcerpc_syntax *a; a = &dce->bi_req.list[i].transfer_syntaxes[j]; if (dcerpc_syntax_supported(a) != -1) { ack = 1; break; } } } if (!ack) { pr_err("Unsupported transfer syntax\n"); ret = dcerpc_bind_nack_return(pipe); } else { ret = dcerpc_bind_ack_return(pipe); } dcerpc_bind_req_free(&dce->bi_req); return ret; } int rpc_restricted_context(struct ksmbd_rpc_command *req) { if (global_conf.restrict_anon == 0) return 0; return req->flags & KSMBD_RPC_RESTRICTED_CONTEXT; } int rpc_ioctl_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp, int max_resp_sz) { int ret; ret = rpc_write_request(req, resp); if (ret == KSMBD_RPC_OK) return rpc_read_request(req, resp, max_resp_sz); return ret; } int rpc_read_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp, int max_resp_sz) { int ret = KSMBD_RPC_ENOTIMPLEMENTED; struct ksmbd_rpc_pipe *pipe; struct ksmbd_dcerpc *dce; pipe = rpc_pipe_lookup(req->handle); if (!pipe || !pipe->dce) { pr_err("RPC: no pipe or pipe has no associated DCE [%d]\n", req->handle); return KSMBD_RPC_EBAD_FID; } dce = pipe->dce; dce->flags &= ~KSMBD_DCERPC_RETURN_READY; dce->rpc_req = req; dce->rpc_resp = resp; dcerpc_set_ext_payload(dce, resp->payload, max_resp_sz); if (dce->hdr.ptype == DCERPC_PTYPE_RPC_BIND || dce->hdr.ptype == DCERPC_PTYPE_RPC_ALTCONT) return dcerpc_bind_return(pipe); if (dce->hdr.ptype != DCERPC_PTYPE_RPC_REQUEST) return KSMBD_RPC_ENOTIMPLEMENTED; if (req->flags & KSMBD_RPC_SRVSVC_METHOD_INVOKE) return rpc_srvsvc_read_request(pipe, resp, max_resp_sz); if (req->flags & KSMBD_RPC_WKSSVC_METHOD_INVOKE) return rpc_wkssvc_read_request(pipe, resp, max_resp_sz); if (req->flags & KSMBD_RPC_SAMR_METHOD_INVOKE) return rpc_samr_read_request(pipe, resp, max_resp_sz); if (req->flags & KSMBD_RPC_LSARPC_METHOD_INVOKE) return rpc_lsarpc_read_request(pipe, resp, max_resp_sz); return ret; } int rpc_write_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp) { struct ksmbd_rpc_pipe *pipe; struct ksmbd_dcerpc *dce; pipe = rpc_pipe_lookup(req->handle); if (!pipe) return KSMBD_RPC_ENOMEM; if (pipe->dce->flags & KSMBD_DCERPC_RETURN_READY) return KSMBD_RPC_OK; if (pipe->num_entries) pr_err("RPC: A call on unflushed pipe. Pending %d\n", pipe->num_entries); dce = pipe->dce; dce->rpc_req = req; dce->rpc_resp = resp; dcerpc_set_ext_payload(dce, req->payload, req->payload_sz); dce->flags |= KSMBD_DCERPC_RETURN_READY; if (dcerpc_hdr_read(dce, &dce->hdr)) return KSMBD_RPC_EBAD_DATA; if (dce->hdr.ptype == DCERPC_PTYPE_RPC_BIND || dce->hdr.ptype == DCERPC_PTYPE_RPC_ALTCONT) return dcerpc_bind_invoke(pipe); if (dce->hdr.ptype != DCERPC_PTYPE_RPC_REQUEST) return KSMBD_RPC_ENOTIMPLEMENTED; if (dcerpc_request_hdr_read(dce, &dce->req_hdr)) return KSMBD_RPC_EBAD_DATA; if (req->flags & KSMBD_RPC_SRVSVC_METHOD_INVOKE) return rpc_srvsvc_write_request(pipe); if (req->flags & KSMBD_RPC_WKSSVC_METHOD_INVOKE) return rpc_wkssvc_write_request(pipe); if (req->flags & KSMBD_RPC_SAMR_METHOD_INVOKE) return rpc_samr_write_request(pipe); if (req->flags & KSMBD_RPC_LSARPC_METHOD_INVOKE) return rpc_lsarpc_write_request(pipe); return KSMBD_RPC_ENOTIMPLEMENTED; } int rpc_open_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp) { struct ksmbd_rpc_pipe *pipe; pipe = rpc_pipe_lookup(req->handle); if (pipe) { pr_err("RPC: pipe ID collision: %d\n", req->handle); return -EEXIST; } pipe = rpc_pipe_alloc_bind(req->handle); if (!pipe) return -ENOMEM; pipe->dce = dcerpc_ext_alloc(KSMBD_DCERPC_LITTLE_ENDIAN | KSMBD_DCERPC_ALIGN4, req->payload, req->payload_sz); if (!pipe->dce) { rpc_pipe_free(pipe); return KSMBD_RPC_ENOMEM; } return KSMBD_RPC_OK; } int rpc_close_request(struct ksmbd_rpc_command *req, struct ksmbd_rpc_command *resp) { struct ksmbd_rpc_pipe *pipe; pipe = rpc_pipe_lookup(req->handle); if (pipe) { rpc_pipe_free(pipe); return 0; } pr_err("RPC: unknown pipe ID: %d\n", req->handle); return KSMBD_RPC_OK; } ksmbd-tools-3.4.4/mountd/rpc_lsarpc.c000066400000000000000000000337271417226526000176030ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 Samsung Electronics Co., Ltd. * * Author(s): Namjae Jeon (linkinjeon@kernel.org) */ #include #include #include #include #include #include #include #include #include #include #include #define LSARPC_OPNUM_DS_ROLE_GET_PRIMARY_DOMAIN_INFO 0 #define LSARPC_OPNUM_OPEN_POLICY2 44 #define LSARPC_OPNUM_QUERY_INFO_POLICY 7 #define LSARPC_OPNUM_LOOKUP_SID2 57 #define LSARPC_OPNUM_LOOKUP_NAMES3 68 #define LSARPC_OPNUM_CLOSE 0 #define DS_ROLE_STANDALONE_SERVER 2 #define DS_ROLE_BASIC_INFORMATION 1 #define LSA_POLICY_INFO_ACCOUNT_DOMAIN 5 static GHashTable *ph_table; static GRWLock ph_table_lock; static gchar *domain_name; static void lsarpc_ph_free(struct policy_handle *ph) { g_rw_lock_writer_lock(&ph_table_lock); g_hash_table_remove(ph_table, &(ph->handle)); g_rw_lock_writer_unlock(&ph_table_lock); free(ph); } static struct policy_handle *lsarpc_ph_lookup(unsigned char *handle) { struct policy_handle *ph; g_rw_lock_reader_lock(&ph_table_lock); ph = g_hash_table_lookup(ph_table, handle); g_rw_lock_reader_unlock(&ph_table_lock); return ph; } static struct policy_handle *lsarpc_ph_alloc(unsigned int id) { struct policy_handle *ph; int ret; ph = calloc(1, sizeof(struct policy_handle)); if (!ph) return NULL; id++; memcpy(ph->handle, &id, sizeof(unsigned int)); g_rw_lock_writer_lock(&ph_table_lock); ret = g_hash_table_insert(ph_table, &(ph->handle), ph); g_rw_lock_writer_unlock(&ph_table_lock); if (!ret) { lsarpc_ph_free(ph); ph = NULL; } return ph; } static int lsa_domain_account_rep(struct ksmbd_dcerpc *dce, char *domain_name) { int len, ret; len = strlen(domain_name); ret = ndr_write_int16(dce, len*2); // length ret |= ndr_write_int16(dce, (len+1)*2); // size dce->num_pointers++; ret |= ndr_write_int32(dce, dce->num_pointers); /* ref pointer for domain name*/ dce->num_pointers++; ret |= ndr_write_int32(dce, dce->num_pointers); /* ref pointer for sid*/ return ret; } static int lsa_domain_account_data(struct ksmbd_dcerpc *dce, char *domain_name, struct smb_sid *sid) { int ret; ret = ndr_write_lsa_string(dce, domain_name); // domain string ret |= ndr_write_int32(dce, sid->num_subauth); // count smb_write_sid(dce, sid); // sid return ret; } static int lsarpc_get_primary_domain_info_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; dce->lr_req.level = ndr_read_int16(dce); return KSMBD_RPC_OK; } static int lsarpc_get_primary_domain_info_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int i; if (dce->lr_req.level != DS_ROLE_BASIC_INFORMATION) return KSMBD_RPC_EBAD_FUNC; dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int16(dce, 1); // count ndr_write_int16(dce, 0); ndr_write_int16(dce, DS_ROLE_STANDALONE_SERVER); // role ndr_write_int16(dce, 0); ndr_write_int32(dce, 0); // flags dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, 0); // NULL pointer : Pointer to Dns Domain ndr_write_int32(dce, 0); // NULL pointer : Pointer to Forest /* NULL Domain guid */ for (i = 0; i < 16; i++) ndr_write_int8(dce, 0); ndr_write_vstring(dce, domain_name); // domain string return KSMBD_RPC_OK; } static int lsarpc_open_policy2_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct policy_handle *ph; ph = lsarpc_ph_alloc(pipe->id); if (!ph) return KSMBD_RPC_ENOMEM; /* write connect handle */ ndr_write_bytes(dce, ph->handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int lsarpc_query_info_policy_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->lr_req.handle, HANDLE_SIZE); dce->lr_req.level = ndr_read_int16(dce); // level return KSMBD_RPC_OK; } static int lsarpc_query_info_policy_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct smb_sid sid; struct policy_handle *ph; ph = lsarpc_ph_lookup(dce->lr_req.handle); if (!ph) return KSMBD_RPC_EBAD_FID; if (dce->lr_req.level != LSA_POLICY_INFO_ACCOUNT_DOMAIN) return KSMBD_RPC_EBAD_FUNC; dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int16(dce, LSA_POLICY_INFO_ACCOUNT_DOMAIN); // level ndr_write_int16(dce, 0); /* Domain, Sid ref pointer */ lsa_domain_account_rep(dce, domain_name); /* Pointer to domain, Sid */ smb_init_domain_sid(&sid); lsa_domain_account_data(dce, domain_name, &sid); return KSMBD_RPC_OK; } static int __lsarpc_entry_processed(struct ksmbd_rpc_pipe *pipe, int i) { gpointer entry; entry = g_array_index(pipe->entries, gpointer, i); pipe->entries = g_array_remove_index(pipe->entries, i); free(entry); return 0; } static int lsarpc_lookup_sid2_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; unsigned int num_sid, i; ndr_read_bytes(dce, dce->lr_req.handle, HANDLE_SIZE); num_sid = ndr_read_int32(dce); ndr_read_int32(dce); // ref pointer ndr_read_int32(dce); // max count for (i = 0; i < num_sid; i++) ndr_read_int32(dce); // ref pointer for (i = 0; i < num_sid; i++) { struct lsarpc_names_info *ni; struct passwd *passwd; int rid; ni = calloc(1, sizeof(struct lsarpc_names_info)); if (!ni) break; ndr_read_int32(dce); // max count smb_read_sid(dce, &ni->sid); // sid ni->sid.num_subauth--; rid = ni->sid.sub_auth[ni->sid.num_subauth]; passwd = getpwuid(rid); if (passwd) ni->user = usm_lookup_user(passwd->pw_name); ni->index = i + 1; if (set_domain_name(&ni->sid, ni->domain_str, &ni->type)) ni->index = -1; pipe->entries = g_array_append_val(pipe->entries, ni); pipe->num_entries++; } pipe->entry_processed = __lsarpc_entry_processed; return KSMBD_RPC_OK; } static int lsarpc_lookup_sid2_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct policy_handle *ph; int i, rc = KSMBD_RPC_OK; ph = lsarpc_ph_lookup(dce->lr_req.handle); if (!ph) return KSMBD_RPC_EBAD_FID; dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, pipe->num_entries); // count dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, 32); // max size ndr_write_int32(dce, pipe->num_entries); // max count for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; ni = (struct lsarpc_names_info *)g_array_index(pipe->entries, gpointer, i); if (ni->type == -1) rc = KSMBD_RPC_SOME_NOT_MAPPED; lsa_domain_account_rep(dce, ni->domain_str); } for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; ni = (struct lsarpc_names_info *)g_array_index(pipe->entries, gpointer, i); lsa_domain_account_data(dce, ni->domain_str, &ni->sid); } /* Pointer to Names */ ndr_write_int32(dce, pipe->num_entries); // count dce->num_pointers++; // ref pointer ndr_write_int32(dce, dce->num_pointers); ndr_write_int32(dce, pipe->num_entries); // max count for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; size_t len; ni = (struct lsarpc_names_info *)g_array_index(pipe->entries, gpointer, i); ndr_write_int16(dce, ni->type); // sid type ndr_write_int16(dce, 0); if (ni->user) len = strlen(ni->user->name); else len = strlen("None"); ndr_write_int16(dce, len*2); // length ndr_write_int16(dce, len*2); // size dce->num_pointers++; // ref pointer ndr_write_int32(dce, dce->num_pointers); ndr_write_int32(dce, ni->index); // sid index ndr_write_int32(dce, 0); // unknown } for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; char *username = "None"; ni = (struct lsarpc_names_info *)g_array_index(pipe->entries, gpointer, i); if (ni->user) username = ni->user->name; ndr_write_string(dce, username); // username } ndr_write_int32(dce, pipe->num_entries); // count if (pipe->entry_processed) { for (i = 0; i < pipe->num_entries; i++) pipe->entry_processed(pipe, 0); pipe->num_entries = 0; } return rc; } static int lsarpc_lookup_names3_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct ndr_uniq_char_ptr username; int num_names, i; ndr_read_bytes(dce, dce->lr_req.handle, HANDLE_SIZE); num_names = ndr_read_int32(dce); // num names ndr_read_int32(dce); // max count for (i = 0; i < num_names; i++) { struct lsarpc_names_info *ni; char *name = NULL; ni = malloc(sizeof(struct lsarpc_names_info)); if (!ni) break; ndr_read_int16(dce); // length ndr_read_int16(dce); // size ndr_read_uniq_vsting_ptr(dce, &username); if (strstr(STR_VAL(username), "\\")) { strtok(STR_VAL(username), "\\"); name = strtok(NULL, "\\"); } ni->user = usm_lookup_user(name); if (!ni->user) break; pipe->entries = g_array_append_val(pipe->entries, ni); pipe->num_entries++; smb_init_domain_sid(&ni->sid); } pipe->entry_processed = __lsarpc_entry_processed; return KSMBD_RPC_OK; } static int lsarpc_lookup_names3_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct policy_handle *ph; struct smb_sid sid; int i; ph = lsarpc_ph_lookup(dce->lr_req.handle); if (!ph) return KSMBD_RPC_EBAD_FID; /* Domain list */ dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, 1); // domain count dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, 32); // max size ndr_write_int32(dce, 1); // max count lsa_domain_account_rep(dce, domain_name); smb_init_domain_sid(&sid); lsa_domain_account_data(dce, domain_name, &sid); ndr_write_int32(dce, pipe->num_entries); // count dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // sid ref id ndr_write_int32(dce, pipe->num_entries); // count for (i = 0; i < pipe->num_entries; i++) { ndr_write_int16(dce, SID_TYPE_USER); // sid type ndr_write_int16(dce, 0); dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, i); // sid index ndr_write_int32(dce, 0); } for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; ni = (struct lsarpc_names_info *)g_array_index(pipe->entries, gpointer, i); ni->sid.sub_auth[ni->sid.num_subauth++] = ni->user->uid; ndr_write_int32(dce, ni->sid.num_subauth); // sid auth count smb_write_sid(dce, &ni->sid); // sid } ndr_write_int32(dce, pipe->num_entries); if (pipe->entry_processed) { for (i = 0; i < pipe->num_entries; i++) pipe->entry_processed(pipe, 0); pipe->num_entries = 0; } return KSMBD_RPC_OK; } static int lsarpc_close_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->lr_req.handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int lsarpc_close_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct policy_handle *ph; ph = lsarpc_ph_lookup(dce->lr_req.handle); if (!ph) return KSMBD_RPC_EBAD_FID; lsarpc_ph_free(ph); ndr_write_int64(dce, 0); ndr_write_int64(dce, 0); ndr_write_int32(dce, 0); return KSMBD_RPC_OK; } static int lsarpc_invoke(struct ksmbd_rpc_pipe *pipe) { int ret = KSMBD_RPC_ENOTIMPLEMENTED; switch (pipe->dce->req_hdr.opnum) { case LSARPC_OPNUM_DS_ROLE_GET_PRIMARY_DOMAIN_INFO || LSARPC_OPNUM_CLOSE: if (pipe->dce->hdr.frag_length == 26) ret = lsarpc_get_primary_domain_info_invoke(pipe); else ret = lsarpc_close_invoke(pipe); break; case LSARPC_OPNUM_OPEN_POLICY2: ret = KSMBD_RPC_OK; break; case LSARPC_OPNUM_QUERY_INFO_POLICY: ret = lsarpc_query_info_policy_invoke(pipe); break; case LSARPC_OPNUM_LOOKUP_SID2: ret = lsarpc_lookup_sid2_invoke(pipe); break; case LSARPC_OPNUM_LOOKUP_NAMES3: ret = lsarpc_lookup_names3_invoke(pipe); break; default: pr_err("LSARPC: unsupported INVOKE method %d, alloc_hint : %d\n", pipe->dce->req_hdr.opnum, pipe->dce->req_hdr.alloc_hint); break; } return ret; } static int lsarpc_return(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz) { struct ksmbd_dcerpc *dce = pipe->dce; int status = KSMBD_RPC_ENOTIMPLEMENTED; dce->offset = sizeof(struct dcerpc_header); dce->offset += sizeof(struct dcerpc_response_header); switch (dce->req_hdr.opnum) { case LSARPC_OPNUM_DS_ROLE_GET_PRIMARY_DOMAIN_INFO || LSARPC_OPNUM_CLOSE: if (dce->hdr.frag_length == 26) status = lsarpc_get_primary_domain_info_return(pipe); else status = lsarpc_close_return(pipe); break; case LSARPC_OPNUM_OPEN_POLICY2: status = lsarpc_open_policy2_return(pipe); break; case LSARPC_OPNUM_QUERY_INFO_POLICY: status = lsarpc_query_info_policy_return(pipe); break; case LSARPC_OPNUM_LOOKUP_SID2: status = lsarpc_lookup_sid2_return(pipe); break; case LSARPC_OPNUM_LOOKUP_NAMES3: status = lsarpc_lookup_names3_return(pipe); break; default: pr_err("LSARPC: unsupported RETURN method %d\n", dce->req_hdr.opnum); status = KSMBD_RPC_EBAD_FUNC; break; } /* * [out] DWORD Return value/code */ ndr_write_int32(dce, status); dcerpc_write_headers(dce, status); dce->rpc_resp->payload_sz = dce->offset; return status; } int rpc_lsarpc_read_request(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz) { return lsarpc_return(pipe, resp, max_resp_sz); } int rpc_lsarpc_write_request(struct ksmbd_rpc_pipe *pipe) { return lsarpc_invoke(pipe); } int rpc_lsarpc_init(void) { char domain_string[NAME_MAX]; /* * ksmbd supports the standalone server and * uses the hostname as the domain name. */ gethostname(domain_string, NAME_MAX); domain_name = g_ascii_strup(domain_string, strlen(domain_string)); ph_table = g_hash_table_new(g_str_hash, g_str_equal); if (!ph_table) return -ENOMEM; g_rw_lock_init(&ph_table_lock); return 0; } void rpc_lsarpc_destroy(void) { g_free(domain_name); if (ph_table) g_hash_table_destroy(ph_table); g_rw_lock_clear(&ph_table_lock); } ksmbd-tools-3.4.4/mountd/rpc_samr.c000066400000000000000000000453221417226526000172530ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 Samsung Electronics Co., Ltd. * * Author(s): Namjae Jeon (linkinjeon@kernel.org) */ #include #include #include #include #include #include #include #include #include #include #define SAMR_OPNUM_CONNECT5 64 #define SAMR_OPNUM_ENUM_DOMAIN 6 #define SAMR_OPNUM_LOOKUP_DOMAIN 5 #define SAMR_OPNUM_OPEN_DOMAIN 7 #define SAMR_OPNUM_LOOKUP_NAMES 17 #define SAMR_OPNUM_OPEN_USER 34 #define SAMR_OPNUM_QUERY_USER_INFO 36 #define SAMR_OPNUM_QUERY_SECURITY 3 #define SAMR_OPNUM_GET_GROUP_FOR_USER 39 #define SAMR_OPNUM_GET_ALIAS_MEMBERSHIP 16 #define SAMR_OPNUM_CLOSE 1 static GHashTable *ch_table; static GRWLock ch_table_lock; static GArray *domain_entries; static gchar *domain_name; static int num_domain_entries; static void samr_ch_free(struct connect_handle *ch) { g_rw_lock_writer_lock(&ch_table_lock); g_hash_table_remove(ch_table, &(ch->handle)); g_rw_lock_writer_unlock(&ch_table_lock); free(ch); } static struct connect_handle *samr_ch_lookup(unsigned char *handle) { struct connect_handle *ch; g_rw_lock_reader_lock(&ch_table_lock); ch = g_hash_table_lookup(ch_table, handle); g_rw_lock_reader_unlock(&ch_table_lock); return ch; } static struct connect_handle *samr_ch_alloc(unsigned int id) { struct connect_handle *ch; int ret; ch = calloc(1, sizeof(struct connect_handle)); if (!ch) return NULL; id++; memcpy(ch->handle, &id, sizeof(unsigned int)); ch->refcount++; g_rw_lock_writer_lock(&ch_table_lock); ret = g_hash_table_insert(ch_table, &(ch->handle), ch); g_rw_lock_writer_unlock(&ch_table_lock); if (!ret) { samr_ch_free(ch); ch = NULL; } return ch; } static int samr_connect5_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct ndr_uniq_char_ptr server_name; ndr_read_uniq_vsting_ptr(dce, &server_name); ndr_read_int32(dce); // Access mask dce->sm_req.level = ndr_read_int32(dce); // level in ndr_read_int32(dce); // Info in dce->sm_req.client_version = ndr_read_int32(dce); return 0; } static int samr_connect5_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; ndr_write_union_int32(dce, dce->sm_req.level); //level out ndr_write_int32(dce, dce->sm_req.client_version); //client version ndr_write_int32(dce, 0); //reserved ch = samr_ch_alloc(pipe->id); if (!ch) return KSMBD_RPC_ENOMEM; /* write connect handle */ ndr_write_bytes(dce, ch->handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int samr_enum_domain_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); return KSMBD_RPC_OK; } int samr_ndr_write_domain_array(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int i, ret = 0; for (i = 0; i < num_domain_entries; i++) { gpointer entry; size_t name_len; ret = ndr_write_int32(dce, i); entry = g_array_index(domain_entries, gpointer, i); name_len = strlen((char *)entry); ret |= ndr_write_int16(dce, name_len*2); ret |= ndr_write_int16(dce, name_len*2); /* ref pointer for name entry */ dce->num_pointers++; ret |= ndr_write_int32(dce, dce->num_pointers); } for (i = 0; i < num_domain_entries; i++) { gpointer entry; entry = g_array_index(domain_entries, gpointer, i); ret |= ndr_write_string(dce, (char *)entry); } return ret; } static int samr_enum_domain_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; int status; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; /* Resume Handle */ ndr_write_int32(dce, 0); dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, num_domain_entries); // Sam entry count dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, num_domain_entries); // Sam max entry count status = samr_ndr_write_domain_array(pipe); /* [out] DWORD* Num Entries */ ndr_write_int32(dce, num_domain_entries); return status; } static int samr_lookup_domain_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); ndr_read_int16(dce); // name len ndr_read_int16(dce); // name size ndr_read_uniq_vsting_ptr(dce, &dce->sm_req.name); // domain name return KSMBD_RPC_OK; } static int samr_lookup_domain_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; struct smb_sid sid = {0}; int i; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); ndr_write_int32(dce, 4); for (i = 0; i < num_domain_entries; i++) { gpointer entry; entry = g_array_index(domain_entries, gpointer, i); if (!strcmp(STR_VAL(dce->sm_req.name), (char *)entry)) { smb_init_domain_sid(&sid); smb_write_sid(dce, &sid); } } return KSMBD_RPC_OK; } static int samr_open_domain_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int samr_open_domain_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; ch->refcount++; ndr_write_bytes(dce, ch->handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int samr_lookup_names_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int user_num; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); user_num = ndr_read_int32(dce); ndr_read_int32(dce); // max count ndr_read_int32(dce); // offset ndr_read_int32(dce); // actual count ndr_read_int16(dce); // name len ndr_read_int16(dce); // name size ndr_read_uniq_vsting_ptr(dce, &dce->sm_req.name); // names return KSMBD_RPC_OK; } static int samr_lookup_names_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; ch->user = usm_lookup_user(STR_VAL(dce->sm_req.name)); if (!ch->user) return KSMBD_RPC_EACCESS_DENIED; ndr_write_int32(dce, 1); // count dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, 1); // count ndr_write_int32(dce, ch->user->uid); // RID ndr_write_int32(dce, 1); dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); ndr_write_int32(dce, 1); ndr_write_int32(dce, 1); return KSMBD_RPC_OK; } static int samr_open_user_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); ndr_read_int32(dce); dce->sm_req.rid = ndr_read_int32(dce); // RID return KSMBD_RPC_OK; } static int samr_open_user_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; ch->refcount++; if (dce->sm_req.rid != ch->user->uid) return KSMBD_RPC_EBAD_FID; ndr_write_bytes(dce, ch->handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int samr_query_user_info_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int samr_query_user_info_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; char *home_dir, *profile_path; char hostname[NAME_MAX]; int home_dir_len, i; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; gethostname(hostname, NAME_MAX); home_dir_len = 2 + strlen(hostname) + 1 + strlen(ch->user->name); home_dir = calloc(1, home_dir_len); if (!home_dir) return KSMBD_RPC_ENOMEM; /* Make Home dir string */ strcpy(home_dir, "\\\\"); strcat(home_dir, hostname); strcat(home_dir, "\\"); strcat(home_dir, ch->user->name); profile_path = calloc(1, home_dir_len + strlen("profile")); if (!profile_path) { free(home_dir); return KSMBD_RPC_ENOMEM; } /* Make Profile path string */ strcat(profile_path, "\\\\"); strcat(profile_path, hostname); strcat(profile_path, "\\"); strcat(profile_path, ch->user->name); strcat(profile_path, "\\"); strcat(profile_path, "profile"); dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int16(dce, 0x15); // info ndr_write_int16(dce, 0); /* * Last Logon/Logoff/Password change, Acct Expiry, * Allow Passworkd Change, Force Password Change. */ for (i = 0; i < 6; i++) ndr_write_int64(dce, 0); ndr_write_int16(dce, strlen(ch->user->name)*2); // account name length ndr_write_int16(dce, strlen(ch->user->name)*2); dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int16(dce, strlen(ch->user->name)*2); // full name length ndr_write_int16(dce, strlen(ch->user->name)*2); dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int16(dce, strlen(home_dir)*2); // home directory length ndr_write_int16(dce, strlen(home_dir)*2); /* Home Drive, Logon Script */ for (i = 0; i < 2; i++) { dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int16(dce, 0); ndr_write_int16(dce, 0); } dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int16(dce, strlen(profile_path)*2); //profile path length ndr_write_int16(dce, strlen(profile_path)*2); /* Description, Workstations, Comments, Parameters */ for (i = 0; i < 4; i++) { dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int16(dce, 0); ndr_write_int16(dce, 0); } dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); /* Lm, Nt, Password and Private*/ for (i = 0; i < 3; i++) { ndr_write_int16(dce, 0); ndr_write_int16(dce, 0); ndr_write_int32(dce, 0); } ndr_write_int32(dce, 0); // buf count /* Pointer to Buffer */ ndr_write_int32(dce, 0); ndr_write_int32(dce, ch->user->uid); // rid ndr_write_int32(dce, 513); // primary gid ndr_write_int32(dce, 0x00000010); // Acct Flags : Acb Normal ndr_write_int32(dce, 0x00FFFFFF); // Fields Present ndr_write_int16(dce, 168); // logon hours ndr_write_int16(dce, 0); /* Pointers to Bits */ dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); //ref pointer /* Bad Password/Logon Count/Country Code/Code Page */ for (i = 0; i < 4; i++) ndr_write_int16(dce, 0); /* Lm/Nt Password Set, Password Expired/etc */ ndr_write_int8(dce, 0); ndr_write_int8(dce, 0); ndr_write_int8(dce, 0); ndr_write_int8(dce, 0); ndr_write_string(dce, ch->user->name); ndr_write_string(dce, ch->user->name); ndr_write_string(dce, home_dir); /* Home Drive, Logon Script */ for (i = 0; i < 2; i++) { ndr_write_int32(dce, 0); ndr_write_int32(dce, 0); ndr_write_int32(dce, 0); } ndr_write_string(dce, profile_path); /* Description, Workstations, Comments, Parameters */ for (i = 0; i < 4; i++) { ndr_write_int32(dce, 0); ndr_write_int32(dce, 0); ndr_write_int32(dce, 0); } /* Logon Hours */ ndr_write_int32(dce, 1260); ndr_write_int32(dce, 0); ndr_write_int32(dce, 21); for (i = 0; i < 21; i++) ndr_write_int8(dce, 0xff); free(home_dir); free(profile_path); return KSMBD_RPC_OK; } static int samr_query_security_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int samr_query_security_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; int sec_desc_len, curr_offset, payload_offset; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; curr_offset = dce->offset; dce->offset += 16; build_sec_desc(dce, &sec_desc_len, ch->user->uid); payload_offset = dce->offset; dce->offset = curr_offset; dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); ndr_write_int32(dce, sec_desc_len); dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); ndr_write_int32(dce, sec_desc_len); dce->offset = payload_offset; return KSMBD_RPC_OK; } static int samr_get_group_for_user_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int samr_get_group_for_user_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, 1); // count dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, 1); // max count ndr_write_int32(dce, 513); // group rid ndr_write_int32(dce, 0x00000007); // attributes return KSMBD_RPC_OK; } static int samr_get_alias_membership_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int samr_get_alias_membership_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; ndr_write_int32(dce, 0); // count dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); // ref pointer ndr_write_int32(dce, 0); // max count return KSMBD_RPC_OK; } static int samr_close_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE); return KSMBD_RPC_OK; } static int samr_close_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; else if (ch->refcount > 1) ch->refcount--; else samr_ch_free(ch); /* write connect handle */ ndr_write_int64(dce, 0); ndr_write_int64(dce, 0); ndr_write_int32(dce, 0); return KSMBD_RPC_OK; } static int samr_invoke(struct ksmbd_rpc_pipe *pipe) { int ret = KSMBD_RPC_ENOTIMPLEMENTED; switch (pipe->dce->req_hdr.opnum) { case SAMR_OPNUM_CONNECT5: ret = samr_connect5_invoke(pipe); break; case SAMR_OPNUM_ENUM_DOMAIN: ret = samr_enum_domain_invoke(pipe); break; case SAMR_OPNUM_LOOKUP_DOMAIN: ret = samr_lookup_domain_invoke(pipe); break; case SAMR_OPNUM_OPEN_DOMAIN: ret = samr_open_domain_invoke(pipe); break; case SAMR_OPNUM_LOOKUP_NAMES: ret = samr_lookup_names_invoke(pipe); break; case SAMR_OPNUM_OPEN_USER: ret = samr_open_user_invoke(pipe); break; case SAMR_OPNUM_QUERY_USER_INFO: ret = samr_query_user_info_invoke(pipe); break; case SAMR_OPNUM_QUERY_SECURITY: ret = samr_query_security_invoke(pipe); break; case SAMR_OPNUM_GET_GROUP_FOR_USER: ret = samr_get_group_for_user_invoke(pipe); break; case SAMR_OPNUM_GET_ALIAS_MEMBERSHIP: ret = samr_get_alias_membership_invoke(pipe); break; case SAMR_OPNUM_CLOSE: ret = samr_close_invoke(pipe); break; default: pr_err("SAMR: unsupported INVOKE method %d\n", pipe->dce->req_hdr.opnum); break; } return ret; } static int samr_return(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz) { struct ksmbd_dcerpc *dce = pipe->dce; int status; /* * Reserve space for response NDR header. We don't know yet if * the payload buffer is big enough. This will determine if we * can set DCERPC_PFC_FIRST_FRAG|DCERPC_PFC_LAST_FRAG or if we * will have a multi-part response. */ dce->offset = sizeof(struct dcerpc_header); dce->offset += sizeof(struct dcerpc_response_header); switch (dce->req_hdr.opnum) { case SAMR_OPNUM_CONNECT5: status = samr_connect5_return(pipe); break; case SAMR_OPNUM_ENUM_DOMAIN: status = samr_enum_domain_return(pipe); break; case SAMR_OPNUM_LOOKUP_DOMAIN: status = samr_lookup_domain_return(pipe); break; case SAMR_OPNUM_OPEN_DOMAIN: status = samr_open_domain_return(pipe); break; case SAMR_OPNUM_LOOKUP_NAMES: status = samr_lookup_names_return(pipe); break; case SAMR_OPNUM_OPEN_USER: status = samr_open_user_return(pipe); break; case SAMR_OPNUM_QUERY_USER_INFO: status = samr_query_user_info_return(pipe); break; case SAMR_OPNUM_QUERY_SECURITY: status = samr_query_security_return(pipe); break; case SAMR_OPNUM_GET_GROUP_FOR_USER: status = samr_get_group_for_user_return(pipe); break; case SAMR_OPNUM_GET_ALIAS_MEMBERSHIP: status = samr_get_alias_membership_return(pipe); break; case SAMR_OPNUM_CLOSE: status = samr_close_return(pipe); break; default: pr_err("SAMR: unsupported RETURN method %d\n", dce->req_hdr.opnum); status = KSMBD_RPC_EBAD_FUNC; break; } if (rpc_restricted_context(dce->rpc_req)) status = KSMBD_RPC_EACCESS_DENIED; /* * [out] DWORD Return value/code */ ndr_write_int32(dce, status); dcerpc_write_headers(dce, status); dce->rpc_resp->payload_sz = dce->offset; return status; } int rpc_samr_read_request(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz) { return samr_return(pipe, resp, max_resp_sz); } int rpc_samr_write_request(struct ksmbd_rpc_pipe *pipe) { return samr_invoke(pipe); } static int rpc_samr_add_domain_entry(char *name) { char *domain_string; domain_string = strdup(name); if (!domain_string) return KSMBD_RPC_ENOMEM; domain_entries = g_array_append_val(domain_entries, domain_string); num_domain_entries++; return 0; } static void rpc_samr_remove_domain_entry(unsigned int eidx) { gpointer entry; entry = g_array_index(domain_entries, gpointer, eidx); domain_entries = g_array_remove_index(domain_entries, eidx); free(entry); } static void domain_entry_free(void *v) { char **entry = v; free(*entry); } int rpc_samr_init(void) { char hostname[NAME_MAX]; ch_table = g_hash_table_new(g_str_hash, g_str_equal); if (!ch_table) return -ENOMEM; domain_entries = g_array_new(0, 0, sizeof(void *)); if (!domain_entries) return -ENOMEM; g_array_set_clear_func(domain_entries, domain_entry_free); /* * ksmbd supports the standalone server and * uses the hostname as the domain name. */ gethostname(hostname, NAME_MAX); domain_name = g_ascii_strup(hostname, strlen(hostname)); rpc_samr_add_domain_entry(domain_name); rpc_samr_add_domain_entry("Builtin"); g_rw_lock_init(&ch_table_lock); return 0; } void rpc_samr_destroy(void) { if (ch_table) g_hash_table_destroy(ch_table); g_rw_lock_clear(&ch_table_lock); num_domain_entries = 0; g_free(domain_name); g_array_free(domain_entries, 1); } ksmbd-tools-3.4.4/mountd/rpc_srvsvc.c000066400000000000000000000265661417226526000176500ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #define SHARE_TYPE_TEMP 0x40000000 #define SHARE_TYPE_HIDDEN 0x80000000 #define SHARE_TYPE_DISKTREE 0 #define SHARE_TYPE_DISKTREE_TEMP (SHARE_TYPE_DISKTREE|SHARE_TYPE_TEMP) #define SHARE_TYPE_DISKTREE_HIDDEN (SHARE_TYPE_DISKTREE|SHARE_TYPE_HIDDEN) #define SHARE_TYPE_PRINTQ 1 #define SHARE_TYPE_PRINTQ_TEMP (SHARE_TYPE_PRINTQ|SHARE_TYPE_TEMP) #define SHARE_TYPE_PRINTQ_HIDDEN (SHARE_TYPE_PRINTQ|SHARE_TYPE_HIDDEN) #define SHARE_TYPE_DEVICE 2 #define SHARE_TYPE_DEVICE_TEMP (SHARE_TYPE_DEVICE|SHARE_TYPE_TEMP) #define SHARE_TYPE_DEVICE_HIDDEN (SHARE_TYPE_DEVICE|SHARE_TYPE_HIDDEN) #define SHARE_TYPE_IPC 3 #define SHARE_TYPE_IPC_TEMP (SHARE_TYPE_IPC|SHARE_TYPE_TEMP) #define SHARE_TYPE_IPC_HIDDEN (SHARE_TYPE_IPC|SHARE_TYPE_HIDDEN) #define SRVSVC_OPNUM_SHARE_ENUM_ALL 15 #define SRVSVC_OPNUM_GET_SHARE_INFO 16 static int __share_type(struct ksmbd_share *share) { if (test_share_flag(share, KSMBD_SHARE_FLAG_PIPE)) return SHARE_TYPE_IPC; if (!g_ascii_strncasecmp(share->name, "IPC", strlen("IPC"))) return SHARE_TYPE_IPC; return SHARE_TYPE_DISKTREE; } static int __share_entry_size_ctr0(struct ksmbd_dcerpc *dce, gpointer entry) { struct ksmbd_share *share = entry; return strlen(share->name) * 2 + 4 * sizeof(__u32); } static int __share_entry_size_ctr1(struct ksmbd_dcerpc *dce, gpointer entry) { struct ksmbd_share *share = entry; int sz = 0; sz = strlen(share->name) * 2; sz += strlen(share->comment) * 2; sz += 9 * sizeof(__u32); return sz; } /* * Embedded Reference Pointers * * An embedded reference pointer is represented in two parts, a 4 octet * value in place and a possibly deferred representation of the referent. */ static int __share_entry_rep_ctr0(struct ksmbd_dcerpc *dce, gpointer entry) { dce->num_pointers++; return ndr_write_int32(dce, dce->num_pointers); /* ref pointer */ } static int __share_entry_rep_ctr1(struct ksmbd_dcerpc *dce, gpointer entry) { struct ksmbd_share *share = entry; int ret; dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); /* ref pointer */ ret |= ndr_write_int32(dce, __share_type(share)); dce->num_pointers++; ret |= ndr_write_int32(dce, dce->num_pointers); /* ref pointer */ return ret; } static int __share_entry_data_ctr0(struct ksmbd_dcerpc *dce, gpointer entry) { struct ksmbd_share *share = entry; return ndr_write_vstring(dce, share->name); } static int __share_entry_data_ctr1(struct ksmbd_dcerpc *dce, gpointer entry) { struct ksmbd_share *share = entry; int ret; ret = ndr_write_vstring(dce, share->name); ret |= ndr_write_vstring(dce, share->comment); return ret; } static int __share_entry_null_rep_ctr0(struct ksmbd_dcerpc *dce, gpointer entry) { return ndr_write_int32(dce, 0); /* ref pointer */ } static int __share_entry_null_rep_ctr1(struct ksmbd_dcerpc *dce, gpointer entry) { int ret; ret = ndr_write_int32(dce, 0); /* ref pointer */ ret |= ndr_write_int32(dce, 0); ret |= ndr_write_int32(dce, 0); /* ref pointer */ return ret; } static int __share_entry_processed(struct ksmbd_rpc_pipe *pipe, int i) { struct ksmbd_share *share; share = g_array_index(pipe->entries, gpointer, i); pipe->entries = g_array_remove_index(pipe->entries, i); pipe->num_entries--; pipe->num_processed++; put_ksmbd_share(share); return 0; } static void __enum_all_shares(gpointer key, gpointer value, gpointer user_data) { struct ksmbd_rpc_pipe *pipe = (struct ksmbd_rpc_pipe *)user_data; struct ksmbd_share *share = (struct ksmbd_share *)value; if (!get_ksmbd_share(share)) return; if (!test_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE)) { put_ksmbd_share(share); return; } if (!test_share_flag(share, KSMBD_SHARE_FLAG_AVAILABLE)) { put_ksmbd_share(share); return; } pipe->entries = g_array_append_val(pipe->entries, share); pipe->num_entries++; } static int srvsvc_share_enum_all_invoke(struct ksmbd_rpc_pipe *pipe) { for_each_ksmbd_share(__enum_all_shares, pipe); pipe->entry_processed = __share_entry_processed; return 0; } static int srvsvc_share_get_info_invoke(struct ksmbd_rpc_pipe *pipe, struct srvsvc_share_info_request *hdr) { struct ksmbd_share *share; int ret; gchar *share_name; share_name = g_ascii_strdown(STR_VAL(hdr->share_name), strlen(STR_VAL(hdr->share_name))); share = shm_lookup_share(share_name); if (!share) return 0; if (!test_share_flag(share, KSMBD_SHARE_FLAG_AVAILABLE)) { put_ksmbd_share(share); return 0; } ret = shm_lookup_hosts_map(share, KSMBD_SHARE_HOSTS_ALLOW_MAP, STR_VAL(hdr->server_name)); if (ret == -ENOENT) { put_ksmbd_share(share); return 0; } if (ret != 0) { gchar *server_name = g_ascii_strdown(STR_VAL(hdr->server_name), strlen(STR_VAL(hdr->server_name))); ret = shm_lookup_hosts_map(share, KSMBD_SHARE_HOSTS_DENY_MAP, server_name); if (ret == 0) { put_ksmbd_share(share); return 0; } } pipe->entries = g_array_append_val(pipe->entries, share); pipe->num_entries++; pipe->entry_processed = __share_entry_processed; return 0; } static int srvsvc_share_enum_all_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int status = KSMBD_RPC_OK; ndr_write_union_int32(dce, dce->si_req.level); status = ndr_write_array_of_structs(pipe); /* * [out] DWORD* TotalEntries * [out, unique] DWORD* ResumeHandle */ ndr_write_int32(dce, pipe->num_processed); if (status == KSMBD_RPC_EMORE_DATA) { dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); ndr_write_int32(dce, 0x01); /* Have pending data, set RETURN_READY again */ dce->flags |= KSMBD_DCERPC_RETURN_READY; } else { dce->num_pointers++; ndr_write_int32(dce, dce->num_pointers); ndr_write_int32(dce, 0); } return status; } static int srvsvc_share_get_info_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_write_union_int32(dce, dce->si_req.level); if (pipe->num_entries) return __ndr_write_array_of_structs(pipe, pipe->num_entries); /* * No data. Either we didn't find the requested net share, * or we didn't even lookup it due to restricted context * rule. In any case, return a zero representation of the * corresponding share_info level. */ if (dce->si_req.level == 0) { dce->entry_rep = __share_entry_null_rep_ctr0; } else if (dce->si_req.level == 1) { dce->entry_rep = __share_entry_null_rep_ctr1; } else { pr_err("Unsupported share info level (read): %d\n", dce->si_req.level); dce->entry_rep = NULL; return KSMBD_RPC_EINVALID_LEVEL; } dce->entry_rep(dce, NULL); if (!rpc_restricted_context(dce->rpc_req)) return KSMBD_RPC_EINVALID_PARAMETER; return KSMBD_RPC_EACCESS_DENIED; } static int srvsvc_parse_share_info_req(struct ksmbd_dcerpc *dce, struct srvsvc_share_info_request *hdr) { ndr_read_uniq_vsting_ptr(dce, &hdr->server_name); if (dce->req_hdr.opnum == SRVSVC_OPNUM_SHARE_ENUM_ALL) { int ptr; /* Read union switch selector */ hdr->level = ndr_read_union_int32(dce); if (hdr->level == -EINVAL) return -EINVAL; ndr_read_int32(dce); // read container pointer ref id ndr_read_int32(dce); // read container array size ptr = ndr_read_int32(dce); // read container array pointer // it should be null if (ptr != 0x00) { pr_err("SRVSVC: container array pointer is %x\n", ptr); return -EINVAL; } hdr->max_size = ndr_read_int32(dce); ndr_read_uniq_ptr(dce, &hdr->payload_handle); return 0; } if (dce->req_hdr.opnum == SRVSVC_OPNUM_GET_SHARE_INFO) { ndr_read_vstring_ptr(dce, &hdr->share_name); hdr->level = ndr_read_int32(dce); return 0; } return -ENOTSUP; } static int srvsvc_share_info_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int ret = KSMBD_RPC_ENOTIMPLEMENTED; if (srvsvc_parse_share_info_req(dce, &dce->si_req)) return KSMBD_RPC_EBAD_DATA; pipe->entry_processed = __share_entry_processed; if (rpc_restricted_context(dce->rpc_req)) return 0; if (dce->req_hdr.opnum == SRVSVC_OPNUM_GET_SHARE_INFO) ret = srvsvc_share_get_info_invoke(pipe, &dce->si_req); if (dce->req_hdr.opnum == SRVSVC_OPNUM_SHARE_ENUM_ALL) ret = srvsvc_share_enum_all_invoke(pipe); return ret; } static int srvsvc_clear_headers(struct ksmbd_rpc_pipe *pipe, int status) { if (status == KSMBD_RPC_EMORE_DATA) return 0; ndr_free_uniq_vsting_ptr(&pipe->dce->si_req.server_name); if (pipe->dce->req_hdr.opnum == SRVSVC_OPNUM_GET_SHARE_INFO) ndr_free_vstring_ptr(&pipe->dce->si_req.share_name); return 0; } static int srvsvc_share_info_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int status = KSMBD_RPC_ENOTIMPLEMENTED; /* * Reserve space for response NDR header. We don't know yet if * the payload buffer is big enough. This will determine if we * can set DCERPC_PFC_FIRST_FRAG|DCERPC_PFC_LAST_FRAG or if we * will have a multi-part response. */ dce->offset = sizeof(struct dcerpc_header); dce->offset += sizeof(struct dcerpc_response_header); pipe->num_processed = 0; if (dce->si_req.level == 0) { dce->entry_size = __share_entry_size_ctr0; dce->entry_rep = __share_entry_rep_ctr0; dce->entry_data = __share_entry_data_ctr0; } else if (dce->si_req.level == 1) { dce->entry_size = __share_entry_size_ctr1; dce->entry_rep = __share_entry_rep_ctr1; dce->entry_data = __share_entry_data_ctr1; } else { pr_err("Unsupported share info level (write): %d\n", dce->si_req.level); rpc_pipe_reset(pipe); } if (dce->req_hdr.opnum == SRVSVC_OPNUM_GET_SHARE_INFO) status = srvsvc_share_get_info_return(pipe); if (dce->req_hdr.opnum == SRVSVC_OPNUM_SHARE_ENUM_ALL) status = srvsvc_share_enum_all_return(pipe); if (rpc_restricted_context(dce->rpc_req)) status = KSMBD_RPC_EACCESS_DENIED; srvsvc_clear_headers(pipe, status); /* * [out] DWORD Return value/code */ ndr_write_int32(dce, status); dcerpc_write_headers(dce, status); dce->rpc_resp->payload_sz = dce->offset; return KSMBD_RPC_OK; } static int srvsvc_invoke(struct ksmbd_rpc_pipe *pipe) { int ret = KSMBD_RPC_ENOTIMPLEMENTED; switch (pipe->dce->req_hdr.opnum) { case SRVSVC_OPNUM_SHARE_ENUM_ALL: case SRVSVC_OPNUM_GET_SHARE_INFO: ret = srvsvc_share_info_invoke(pipe); break; default: pr_err("SRVSVC: unsupported INVOKE method %d\n", pipe->dce->req_hdr.opnum); break; } return ret; } static int srvsvc_return(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz) { struct ksmbd_dcerpc *dce = pipe->dce; int ret; switch (dce->req_hdr.opnum) { case SRVSVC_OPNUM_SHARE_ENUM_ALL: if (dce->si_req.max_size < (unsigned int)max_resp_sz) max_resp_sz = dce->si_req.max_size; /* Fall through */ case SRVSVC_OPNUM_GET_SHARE_INFO: dcerpc_set_ext_payload(dce, resp->payload, max_resp_sz); ret = srvsvc_share_info_return(pipe); break; default: pr_err("SRVSVC: unsupported RETURN method %d\n", dce->req_hdr.opnum); ret = KSMBD_RPC_EBAD_FUNC; break; } return ret; } int rpc_srvsvc_read_request(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz) { return srvsvc_return(pipe, resp, max_resp_sz); } int rpc_srvsvc_write_request(struct ksmbd_rpc_pipe *pipe) { return srvsvc_invoke(pipe); } ksmbd-tools-3.4.4/mountd/rpc_wkssvc.c000066400000000000000000000117711417226526000176320ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #define WKSSVC_NETWKSTA_GET_INFO (0) #define WKSSVC_PLATFORM_ID_DOS 300 #define WKSSVC_PLATFORM_ID_OS2 400 #define WKSSVC_PLATFORM_ID_NT 500 #define WKSSVC_PLATFORM_ID_OSF 600 #define WKSSVC_PLATFORM_ID_VMS 700 #define WKSSVC_VERSION_MAJOR 0x2 #define WKSSVC_VERSION_MINOR 0x1 static int wkssvc_clear_headers(struct ksmbd_rpc_pipe *pipe, int status) { ndr_free_uniq_vsting_ptr(&pipe->dce->wi_req.server_name); return 0; } static int __netwksta_entry_rep_ctr100(struct ksmbd_dcerpc *dce, gpointer entry) { int ret = 0; /* srvsvc_PlatformId */ ret |= ndr_write_int32(dce, WKSSVC_PLATFORM_ID_NT); /* server_name */ dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); /* ref pointer */ dce->num_pointers++; /* domain_name */ ret |= ndr_write_int32(dce, dce->num_pointers); /* ref pointer */ /* version_major */ ret |= ndr_write_int32(dce, WKSSVC_VERSION_MAJOR); /* version_minor */ ret |= ndr_write_int32(dce, WKSSVC_VERSION_MINOR); return ret; } static int __netwksta_entry_data_ctr100(struct ksmbd_dcerpc *dce, gpointer entry) { int ret = 0; /* * Umm... Hmm... Huh... */ ret |= ndr_write_vstring(dce, STR_VAL(dce->wi_req.server_name)); ret |= ndr_write_vstring(dce, global_conf.work_group); return ret; } static int wkssvc_netwksta_get_info_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; ndr_write_union_int32(dce, dce->wi_req.level); if (dce->wi_req.level != 100) { pr_err("Unsupported wksta info level (read): %d\n", dce->wi_req.level); dce->entry_rep = NULL; return KSMBD_RPC_EINVALID_LEVEL; } dce->entry_rep(dce, NULL); dce->entry_data(dce, NULL); return KSMBD_RPC_OK; } static int wkssvc_netwksta_info_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int status = KSMBD_RPC_ENOTIMPLEMENTED; /* * Reserve space for response NDR header. We don't know yet if * the payload buffer is big enough. This will determine if we * can set DCERPC_PFC_FIRST_FRAG|DCERPC_PFC_LAST_FRAG or if we * will have a multi-part response. */ dce->offset = sizeof(struct dcerpc_header); dce->offset += sizeof(struct dcerpc_response_header); pipe->num_processed = 0; if (dce->wi_req.level == 100) { dce->entry_rep = __netwksta_entry_rep_ctr100; dce->entry_data = __netwksta_entry_data_ctr100; } else { pr_err("Unsupported wksta info level (write): %d\n", dce->wi_req.level); rpc_pipe_reset(pipe); } if (dce->req_hdr.opnum == WKSSVC_NETWKSTA_GET_INFO) status = wkssvc_netwksta_get_info_return(pipe); if (rpc_restricted_context(dce->rpc_req)) status = KSMBD_RPC_EACCESS_DENIED; wkssvc_clear_headers(pipe, status); /* * [out] DWORD Return value/code */ ndr_write_int32(dce, status); dcerpc_write_headers(dce, status); dce->rpc_resp->payload_sz = dce->offset; return KSMBD_RPC_OK; } static int wkssvc_netwksta_get_info_invoke(struct ksmbd_rpc_pipe *pipe, struct wkssvc_netwksta_info_request *hdr) { return KSMBD_RPC_OK; } static int wkssvc_parse_netwksta_info_req(struct ksmbd_dcerpc *dce, struct wkssvc_netwksta_info_request *hdr) { ndr_read_uniq_vsting_ptr(dce, &hdr->server_name); hdr->level = ndr_read_int32(dce); return 0; } static int wkssvc_netwksta_info_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int ret = KSMBD_RPC_ENOTIMPLEMENTED; if (wkssvc_parse_netwksta_info_req(dce, &dce->wi_req)) return KSMBD_RPC_EBAD_DATA; if (rpc_restricted_context(dce->rpc_req)) return KSMBD_RPC_OK; if (dce->req_hdr.opnum == WKSSVC_NETWKSTA_GET_INFO) ret = wkssvc_netwksta_get_info_invoke(pipe, &dce->wi_req); return ret; } static int wkssvc_invoke(struct ksmbd_rpc_pipe *pipe) { int ret = KSMBD_RPC_ENOTIMPLEMENTED; switch (pipe->dce->req_hdr.opnum) { case WKSSVC_NETWKSTA_GET_INFO: ret = wkssvc_netwksta_info_invoke(pipe); break; default: pr_debug("WKSSVC: unsupported INVOKE method %d\n", pipe->dce->req_hdr.opnum); break; } return ret; } static int wkssvc_return(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz) { struct ksmbd_dcerpc *dce = pipe->dce; int ret; switch (dce->req_hdr.opnum) { case WKSSVC_NETWKSTA_GET_INFO: dcerpc_set_ext_payload(dce, resp->payload, max_resp_sz); ret = wkssvc_netwksta_info_return(pipe); break; default: pr_err("WKSSVC: unsupported RETURN method %d\n", dce->req_hdr.opnum); ret = KSMBD_RPC_EBAD_FUNC; break; } return ret; } int rpc_wkssvc_read_request(struct ksmbd_rpc_pipe *pipe, struct ksmbd_rpc_command *resp, int max_resp_sz) { return wkssvc_return(pipe, resp, max_resp_sz); } int rpc_wkssvc_write_request(struct ksmbd_rpc_pipe *pipe) { return wkssvc_invoke(pipe); } ksmbd-tools-3.4.4/mountd/smbacl.c000066400000000000000000000153741417226526000167120ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1+ /* * Copyright (c) International Business Machines Corp., 2007 * Author(s): Steve French (sfrench@us.ibm.com) * Copyright (C) 2020 Samsung Electronics Co., Ltd. * Author(s): Namjae Jeon (linkinjeon@kernel.org) */ #include #include #include #include static const struct smb_sid sid_domain = {1, 1, {0, 0, 0, 0, 0, 5}, {21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; /* security id for everyone/world system group */ static const struct smb_sid sid_everyone = { 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; /* S-1-22-1 Unmapped Unix users */ static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; /* S-1-22-2 Unmapped Unix groups */ static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; /* security id for local group */ static const struct smb_sid sid_local_group = { 1, 1, {0, 0, 0, 0, 0, 5}, {32} }; void smb_read_sid(struct ksmbd_dcerpc *dce, struct smb_sid *sid) { int i; sid->revision = ndr_read_int8(dce); sid->num_subauth = ndr_read_int8(dce); for (i = 0; i < NUM_AUTHS; ++i) sid->authority[i] = ndr_read_int8(dce); for (i = 0; i < sid->num_subauth; ++i) sid->sub_auth[i] = ndr_read_int32(dce); } void smb_write_sid(struct ksmbd_dcerpc *dce, const struct smb_sid *src) { int i; ndr_write_int8(dce, src->revision); ndr_write_int8(dce, src->num_subauth); for (i = 0; i < NUM_AUTHS; ++i) ndr_write_int8(dce, src->authority[i]); for (i = 0; i < src->num_subauth; ++i) ndr_write_int32(dce, src->sub_auth[i]); } void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) { int i; dst->revision = src->revision; dst->num_subauth = src->num_subauth; for (i = 0; i < NUM_AUTHS; ++i) dst->authority[i] = src->authority[i]; for (i = 0; i < dst->num_subauth; ++i) dst->sub_auth[i] = src->sub_auth[i]; } void smb_init_domain_sid(struct smb_sid *sid) { int i; memset(sid, 0, sizeof(struct smb_sid)); sid->revision = 1; sid->num_subauth = 4; sid->authority[5] = 5; sid->sub_auth[0] = 21; for (i = 0; i < 3; i++) sid->sub_auth[i+1] = global_conf.gen_subauth[i]; } int smb_compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) { int i; int num_subauth, num_sat, num_saw; if ((!ctsid) || (!cwsid)) return 1; /* compare the revision */ if (ctsid->revision != cwsid->revision) { if (ctsid->revision > cwsid->revision) return 1; else return -1; } /* compare all of the six auth values */ for (i = 0; i < NUM_AUTHS; ++i) { if (ctsid->authority[i] != cwsid->authority[i]) { if (ctsid->authority[i] > cwsid->authority[i]) return 1; else return -1; } } /* compare all of the subauth values if any */ num_sat = ctsid->num_subauth; num_saw = cwsid->num_subauth; num_subauth = num_sat < num_saw ? num_sat : num_saw; if (num_subauth) { for (i = 0; i < num_subauth; ++i) { if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { if (ctsid->sub_auth[i] > cwsid->sub_auth[i]) return 1; else return -1; } } } return 0; /* sids compare/match */ } static void smb_sid_to_string(char *domain, struct smb_sid *sid) { char str[PATH_MAX]; int i, domain_len = 0, len; strncpy(domain, "S-", 2); domain_len += 2; len = g_sprintf(str, "%i", (int)sid->revision); strncpy(&domain[domain_len], str, len); domain_len += len; domain[domain_len++] = '-'; len = g_sprintf(str, "%i", (int)sid->authority[5]); strncpy(&domain[domain_len], str, len); domain_len += len; for (i = 0; i < sid->num_subauth; i++) { domain[domain_len++] = '-'; len = g_sprintf(str, "%u", sid->sub_auth[i]); strncpy(&domain[domain_len], str, len); domain_len += len; } } int set_domain_name(struct smb_sid *sid, char *domain, int *type) { int ret = 0; char domain_string[NAME_MAX] = {0}; gchar *domain_name; if (!smb_compare_sids(sid, &sid_domain) && !memcmp(&sid->sub_auth[1], global_conf.gen_subauth, sizeof(__u32) * 3)) { gethostname(domain_string, NAME_MAX); domain_name = g_ascii_strup(domain_string, strlen(domain_string)); if (!domain_name) return -ENOMEM; strcpy(domain, domain_name); g_free(domain_name); *type = SID_TYPE_USER; } else if (!smb_compare_sids(sid, &sid_unix_users)) { strcpy(domain, "Unix User"); *type = SID_TYPE_USER; } else if (!smb_compare_sids(sid, &sid_unix_groups)) { strcpy(domain, "Unix Group"); *type = SID_TYPE_GROUP; } else { smb_sid_to_string(domain_string, sid); domain_name = g_ascii_strup(domain_string, strlen(domain_string)); if (!domain_name) return -ENOMEM; strcpy(domain, domain_name); g_free(domain_name); *type = SID_TYPE_UNKNOWN; ret = -ENOENT; } return ret; } static int smb_set_ace(struct ksmbd_dcerpc *dce, int access_req, int rid, const struct smb_sid *rsid) { int size; struct smb_sid sid = {0}; memcpy(&sid, rsid, sizeof(struct smb_sid)); ndr_write_int8(dce, ACCESS_ALLOWED); // ace type ndr_write_int8(dce, 0); // ace flags size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid.num_subauth * 4); if (rid) size += 4; ndr_write_int16(dce, size); // ace size ndr_write_int32(dce, access_req); // ace access required if (rid) sid.sub_auth[sid.num_subauth++] = rid; smb_write_sid(dce, &sid); return size; } static int set_dacl(struct ksmbd_dcerpc *dce, int rid) { int size = 0, i; struct smb_sid owner_domain; /* Other */ size += smb_set_ace(dce, 0x0002035b, 0, &sid_everyone); /* Local Group Administrators */ size += smb_set_ace(dce, 0x000f07ff, 544, &sid_local_group); /* Local Group Account Operators */ size += smb_set_ace(dce, 0x000f07ff, 548, &sid_local_group); /* Owner RID */ memcpy(&owner_domain, &sid_domain, sizeof(struct smb_sid)); for (i = 0; i < 3; ++i) { owner_domain.sub_auth[i + 1] = global_conf.gen_subauth[i]; owner_domain.num_subauth++; } size += smb_set_ace(dce, 0x00020044, rid, &owner_domain); return size; } int build_sec_desc(struct ksmbd_dcerpc *dce, __u32 *secdesclen, int rid) { int l_offset, acl_size_offset; int acl_size; /* NT Security Descrptor : Revision */ ndr_write_int16(dce, 1); /* ACL Type */ ndr_write_int16(dce, SELF_RELATIVE | DACL_PRESENT); /* Offset to owner SID */ ndr_write_int32(dce, 0); /* Offset to group SID */ ndr_write_int32(dce, 0); /* Offset to SACL */ ndr_write_int32(dce, 0); /* Offset to DACL */ ndr_write_int32(dce, sizeof(struct smb_ntsd)); /* DACL Revision */ ndr_write_int16(dce, 2); acl_size_offset = dce->offset; dce->offset += 2; /* Number of ACEs */ ndr_write_int32(dce, 4); acl_size = set_dacl(dce, rid) + sizeof(struct smb_acl); /* ACL Size */ l_offset = dce->offset; dce->offset = acl_size_offset; ndr_write_int16(dce, acl_size); dce->offset = l_offset; *secdesclen = sizeof(struct smb_ntsd) + acl_size; return 0; } ksmbd-tools-3.4.4/mountd/worker.c000066400000000000000000000176401417226526000167600ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_WORKER_THREADS 4 static GThreadPool *pool; #define VALID_IPC_MSG(m, t) \ ({ \ int ret = 1; \ if (((m)->sz != sizeof(t))) { \ pr_err("Bad message: %s\n", __func__); \ ret = 0; \ } \ ret; \ }) static int login_request(struct ksmbd_ipc_msg *msg) { struct ksmbd_login_request *req; struct ksmbd_login_response *resp; struct ksmbd_ipc_msg *resp_msg; resp_msg = ipc_msg_alloc(sizeof(*resp)); if (!resp_msg) goto out; req = KSMBD_IPC_MSG_PAYLOAD(msg); resp = KSMBD_IPC_MSG_PAYLOAD(resp_msg); resp->status = KSMBD_USER_FLAG_INVALID; if (VALID_IPC_MSG(msg, struct ksmbd_login_request)) usm_handle_login_request(req, resp); resp_msg->type = KSMBD_EVENT_LOGIN_RESPONSE; resp->handle = req->handle; ipc_msg_send(resp_msg); out: ipc_msg_free(resp_msg); return 0; } static int spnego_authen_request(struct ksmbd_ipc_msg *msg) { struct ksmbd_spnego_authen_request *req; struct ksmbd_spnego_authen_response *resp; struct ksmbd_ipc_msg *resp_msg = NULL; struct ksmbd_spnego_auth_out auth_out; struct ksmbd_login_request login_req; int retval = 0; req = KSMBD_IPC_MSG_PAYLOAD(msg); resp_msg = ipc_msg_alloc(sizeof(*resp)); if (!resp_msg) return -ENOMEM; resp_msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE; resp = KSMBD_IPC_MSG_PAYLOAD(resp_msg); resp->handle = req->handle; resp->login_response.status = KSMBD_USER_FLAG_INVALID; if (msg->sz <= sizeof(struct ksmbd_spnego_authen_request)) { retval = -EINVAL; goto out; } /* Authentication */ if (spnego_handle_authen_request(req, &auth_out) != 0) { retval = -EPERM; goto out; } ipc_msg_free(resp_msg); resp_msg = ipc_msg_alloc(sizeof(*resp) + auth_out.key_len + auth_out.blob_len); if (!resp_msg) { retval = -ENOMEM; goto out_free_auth; } resp_msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE; resp = KSMBD_IPC_MSG_PAYLOAD(resp_msg); resp->handle = req->handle; resp->login_response.status = KSMBD_USER_FLAG_INVALID; /* login */ login_req.handle = req->handle; strncpy(login_req.account, auth_out.user_name, sizeof(login_req.account)); usm_handle_login_request(&login_req, &resp->login_response); if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) { pr_info("failed to login %s\n", login_req.account); goto out_free_auth; } resp->session_key_len = auth_out.key_len; memcpy(resp->payload, auth_out.sess_key, auth_out.key_len); resp->spnego_blob_len = auth_out.blob_len; memcpy(resp->payload + auth_out.key_len, auth_out.spnego_blob, auth_out.blob_len); out_free_auth: free(auth_out.spnego_blob); free(auth_out.sess_key); free(auth_out.user_name); out: if (resp_msg) { ipc_msg_send(resp_msg); ipc_msg_free(resp_msg); } return retval; } static int tree_connect_request(struct ksmbd_ipc_msg *msg) { struct ksmbd_tree_connect_request *req; struct ksmbd_tree_connect_response *resp; struct ksmbd_ipc_msg *resp_msg; resp_msg = ipc_msg_alloc(sizeof(*resp)); if (!resp_msg) goto out; req = KSMBD_IPC_MSG_PAYLOAD(msg); resp = KSMBD_IPC_MSG_PAYLOAD(resp_msg); resp->status = KSMBD_TREE_CONN_STATUS_ERROR; resp->connection_flags = 0; if (VALID_IPC_MSG(msg, struct ksmbd_tree_connect_request)) tcm_handle_tree_connect(req, resp); resp_msg->type = KSMBD_EVENT_TREE_CONNECT_RESPONSE; resp->handle = req->handle; ipc_msg_send(resp_msg); out: ipc_msg_free(resp_msg); return 0; } static int share_config_request(struct ksmbd_ipc_msg *msg) { struct ksmbd_share_config_request *req; struct ksmbd_share_config_response *resp; struct ksmbd_share *share = NULL; struct ksmbd_ipc_msg *resp_msg; int payload_sz = 0; req = KSMBD_IPC_MSG_PAYLOAD(msg); if (VALID_IPC_MSG(msg, struct ksmbd_share_config_request)) { share = shm_lookup_share(req->share_name); if (share) payload_sz = shm_share_config_payload_size(share); } resp_msg = ipc_msg_alloc(sizeof(*resp) + payload_sz); if (!resp_msg) goto out; resp = KSMBD_IPC_MSG_PAYLOAD(resp_msg); shm_handle_share_config_request(share, resp); resp_msg->type = KSMBD_EVENT_SHARE_CONFIG_RESPONSE; resp->handle = req->handle; ipc_msg_send(resp_msg); out: put_ksmbd_share(share); ipc_msg_free(resp_msg); return 0; } static int tree_disconnect_request(struct ksmbd_ipc_msg *msg) { struct ksmbd_tree_disconnect_request *req; if (!VALID_IPC_MSG(msg, struct ksmbd_tree_disconnect_request)) return -EINVAL; req = KSMBD_IPC_MSG_PAYLOAD(msg); tcm_handle_tree_disconnect(req->session_id, req->connect_id); return 0; } static int logout_request(struct ksmbd_ipc_msg *msg) { if (!VALID_IPC_MSG(msg, struct ksmbd_logout_request)) return -EINVAL; return usm_handle_logout_request(KSMBD_IPC_MSG_PAYLOAD(msg)); } static int heartbeat_request(struct ksmbd_ipc_msg *msg) { if (!VALID_IPC_MSG(msg, struct ksmbd_heartbeat)) return -EINVAL; pr_debug("HEARTBEAT frame from the server\n"); return 0; } static int rpc_request(struct ksmbd_ipc_msg *msg) { struct ksmbd_rpc_command *req; struct ksmbd_rpc_command *resp; struct ksmbd_ipc_msg *resp_msg; int ret = -ENOTSUP; req = KSMBD_IPC_MSG_PAYLOAD(msg); if (req->flags & KSMBD_RPC_METHOD_RETURN) resp_msg = ipc_msg_alloc(KSMBD_IPC_MAX_MESSAGE_SIZE - sizeof(struct ksmbd_rpc_command)); else resp_msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); if (!resp_msg) goto out; resp = KSMBD_IPC_MSG_PAYLOAD(resp_msg); if ((req->flags & KSMBD_RPC_RAP_METHOD) == KSMBD_RPC_RAP_METHOD) { pr_err("RAP command is not supported yet %x\n", req->flags); ret = KSMBD_RPC_ENOTIMPLEMENTED; } else if (req->flags & KSMBD_RPC_OPEN_METHOD) { ret = rpc_open_request(req, resp); } else if (req->flags & KSMBD_RPC_CLOSE_METHOD) { ret = rpc_close_request(req, resp); } else if (req->flags & KSMBD_RPC_IOCTL_METHOD) { ret = rpc_ioctl_request(req, resp, resp_msg->sz); } else if (req->flags & KSMBD_RPC_WRITE_METHOD) { ret = rpc_write_request(req, resp); } else if (req->flags & KSMBD_RPC_READ_METHOD) { ret = rpc_read_request(req, resp, resp_msg->sz); } else { pr_err("Unknown RPC method: %x\n", req->flags); ret = KSMBD_RPC_ENOTIMPLEMENTED; } resp_msg->type = KSMBD_EVENT_RPC_RESPONSE; resp->handle = req->handle; resp->flags = ret; resp_msg->sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz; ipc_msg_send(resp_msg); out: ipc_msg_free(resp_msg); return 0; } static void worker_pool_fn(gpointer event, gpointer user_data) { struct ksmbd_ipc_msg *msg = (struct ksmbd_ipc_msg *)event; switch (msg->type) { case KSMBD_EVENT_LOGIN_REQUEST: login_request(msg); break; case KSMBD_EVENT_TREE_CONNECT_REQUEST: tree_connect_request(msg); break; case KSMBD_EVENT_TREE_DISCONNECT_REQUEST: tree_disconnect_request(msg); break; case KSMBD_EVENT_LOGOUT_REQUEST: logout_request(msg); break; case KSMBD_EVENT_SHARE_CONFIG_REQUEST: share_config_request(msg); break; case KSMBD_EVENT_RPC_REQUEST: rpc_request(msg); break; case KSMBD_EVENT_HEARTBEAT_REQUEST: heartbeat_request(msg); break; case KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST: spnego_authen_request(msg); break; default: pr_err("Unknown IPC message type: %d\n", msg->type); break; } ipc_msg_free(msg); } int wp_ipc_msg_push(struct ksmbd_ipc_msg *msg) { return g_thread_pool_push(pool, msg, NULL); } void wp_destroy(void) { if (pool) g_thread_pool_free(pool, 1, 1); } int wp_init(void) { GError *err; pool = g_thread_pool_new(worker_pool_fn, NULL, MAX_WORKER_THREADS, 0, &err); if (!pool) { if (err) { pr_err("Can't create pool: %s\n", err->message); g_error_free(err); } goto out_error; } return 0; out_error: wp_destroy(); return -ENOMEM; } ksmbd-tools-3.4.4/scripts/000077500000000000000000000000001417226526000154545ustar00rootroot00000000000000ksmbd-tools-3.4.4/scripts/install.sh000077500000000000000000000004001417226526000174530ustar00rootroot00000000000000#!/bin/sh mkdir -p ~/.config/systemd/user cp usmbd.service ~/.config/systemd/user systemctl --user daemon-reload echo "Run 'systemctl --user start usmbd.service' to start the service" echo "Run 'systemctl --user start usmbd.service' to stop the service" ksmbd-tools-3.4.4/smb.conf.example000066400000000000000000000011421417226526000170450ustar00rootroot00000000000000;****************************************************************************** ; Rules to update this file: ; - Every [share] definition should start on new line ; - Every parameter should be indented with single tab ; - There should be single spaces around equal (eg: " = ") ; - Multiple parameters should be separated with comma ; eg: "invalid users = usr1,usr2,usr3" ; ; Make sure to configure the server after making changes to this file. ;****************************************************************************** [global] netbios name = SMBD [homes] comment = content server share path = /tmp