pax_global_header00006660000000000000000000000064146041034270014513gustar00rootroot0000000000000052 comment=16821df83e3834fda6a58fedff1bcdb3638febf5 ksmbd-tools-3.5.2/000077500000000000000000000000001460410342700137605ustar00rootroot00000000000000ksmbd-tools-3.5.2/.gitignore000066400000000000000000000013041460410342700157460ustar00rootroot00000000000000# automake Makefile.in /ar-lib /mdate-sh /py-compile /test-driver /ylwrap .deps/ .dirstamp # autoconf autom4te.cache build-aux /autoscan.log /autoscan-*.log /aclocal.m4 /compile /config.cache /config.guess /config.h.in /config.h /config.log /config.status /config.sub /configure /configure.scan /depcomp /install-sh /missing /stamp-h1 # libtool /libtool /ltmain.sh # texinfo /texinfo.tex # m4 m4/libtool.m4 m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 # Generated by autotools Makefile # Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Executables ksmbd.* ksmbdctl ksmbd-tools-3.5.2/.travis.yml000066400000000000000000000027471460410342700161030ustar00rootroot00000000000000dist: jammy language: c notifications: - email: true before_install: - sudo apt-get install libnl-3-dev libnl-genl-3-dev krb5-multidev heimdal-multidev ninja-build - gcc --version - g++ --version - pip3 install --user meson - PATH=$HOME/.local/bin:$PATH jobs: include: - before_script: - ./autogen.sh - ./configure name: "autotools build without krb5" script: - make DISTCHECK_CONFIGURE_FLAGS=--enable-krb5=no distcheck - before_script: - ./autogen.sh - ./configure name: "autotools build with mit krb5" script: - make DISTCHECK_CONFIGURE_FLAGS="LIBKRB5_CFLAGS='$(krb5-config.mit --cflags)' LIBKRB5_LIBS='$(krb5-config.mit --libs)' --enable-krb5" distcheck - before_script: - ./autogen.sh - ./configure name: "autotools build with heimdal krb5" script: - make DISTCHECK_CONFIGURE_FLAGS="LIBKRB5_CFLAGS='$(krb5-config.heimdal --cflags)' LIBKRB5_LIBS='$(krb5-config.heimdal --libs) -lasn1' --enable-krb5" distcheck - before_script: - mkdir build - cd build name: "meson build without krb5" script: - meson -Dkrb5=disabled .. - meson dist - before_script: - mkdir build - cd build name: "meson build with mit krb5" script: - meson -Dkrb5=enabled .. - meson dist - before_script: - mkdir build - cd build name: "meson build with heimdal krb5" script: - meson -Dkrb5=enabled -Dkrb5_name=heimdal-krb5 .. - meson dist ksmbd-tools-3.5.2/AUTHORS000066400000000000000000000017321460410342700150330ustar00rootroot00000000000000Original Author and Creator =========================== Namjae Jeon Maintainers =============== Namjae Jeon Patch Contributors and Developers --------------------------------- Atte Heikkilä Marios Makassikis Rosen Penev Sergey Senozhatsky (Resolve overall architecture issues and make close to the mainline. and his work cause ksmbd 2.0 to be reborn) Taeyang Mok (various ksmbd 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.5.2/COPYING000066400000000000000000000432341460410342700150210ustar00rootroot00000000000000 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.5.2/Makefile.am000066400000000000000000000012461460410342700160170ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 SUBDIRS = addshare adduser control mountd tools EXTRA_DIST = include \ README.md \ ksmbd-tools.spec \ ksmbd.conf.5.in \ ksmbdpwd.db.5.in \ ksmbd.service.in \ meson.build \ meson_options.txt pkgsysconfdir = $(sysconfdir)/ksmbd dist_pkgsysconf_DATA = ksmbd.conf.example man_MANS = ksmbd.conf.5 ksmbdpwd.db.5 systemdsystemunit_DATA = ksmbd.service $(man_MANS) $(systemdsystemunit_DATA): %: %.in; @$(in_script) $< >$@ CLEANFILES = $(man_MANS) $(systemdsystemunit_DATA) AM_DISTCHECK_CONFIGURE_FLAGS = \ --with-systemdsystemunitdir=$${dc_install_base}/$(systemdsystemunitdir) ksmbd-tools-3.5.2/PFIF.txt000066400000000000000000000002721460410342700152460ustar00rootroot00000000000000This 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.5.2/README.md000066400000000000000000000160521460410342700152430ustar00rootroot00000000000000# ksmbd-tools [![Build Status](https://app.travis-ci.com/cifsd-team/ksmbd-tools.svg?branch=master)](https://app.travis-ci.com/cifsd-team/ksmbd-tools) [![License](https://img.shields.io/badge/License-GPL_v2-blue.svg)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) [ksmbd-tools](https://github.com/cifsd-team/ksmbd-tools) is a collection of userspace utilities for [the ksmbd kernel server](https://www.kernel.org/doc/html/latest/filesystems/cifs/ksmbd.html) merged to mainline in the Linux 5.15 release. ## Table of Contents - [Building and Installing](#building-and-installing) - [Usage](#usage) - [Packages](#packages) ## Building and Installing You should first check if your distribution has a package for ksmbd-tools, and if that is the case, consider installing it from the package manager. Otherwise, follow these instructions to build it yourself. Either the GNU Autotools or Meson build system can be used. Dependencies for Debian and its derivatives: `git` `gcc` `pkgconf` `autoconf` `automake` `libtool` `make` `meson` `ninja-build` `gawk` `libnl-3-dev` `libnl-genl-3-dev` `libglib2.0-dev` Dependencies for RHEL and its derivatives: `git` `gcc` `pkgconf` `autoconf` `automake` `libtool` `make` `meson` `ninja-build` `gawk` `libnl3-devel` `glib2-devel` Example build and install: ```sh git clone https://github.com/cifsd-team/ksmbd-tools.git cd ksmbd-tools # autotools build ./autogen.sh ./configure --with-rundir=/run make sudo make install # meson build mkdir build cd build meson -Drundir=/run .. ninja sudo ninja install ``` By default, the utilities are in `/usr/local/sbin` and the files they use by default are under `/usr/local/etc` in the `ksmbd` directory. If you would like to install ksmbd-tools under `/usr`, where it may conflict with ksmbd-tools installed using the package manager, give `--prefix=/usr` and `--sysconfdir=/etc` as options to `configure` or `meson`. In that case, the utilities are in `/usr/sbin` and the files they use by default are under `/etc` in the `ksmbd` directory. It is likely that you should give `--with-rundir` or `-Drundir` as an option to `configure` or `meson`, respectively. This is due to it being likely that your system does not mount a tmpfs filesystem at the directory given by the default value. Common choices are `/run`, `/var/run`, or `/tmp`. ksmbd-tools uses the directory for per-process modifiable data, namely the `ksmbd.lock` file holding the PID of the `ksmbd.mountd` manager process. If your autoconf supports it, you may instead choose to give `--runstatedir` to `configure`. If you have systemd and it meets at least the minimum version required, the build will install the `ksmbd.service` unit file. The unit file supports the usual unit commands and handles loading of the kernel module. Note that the location of the unit file may conflict with ksmbd-tools installed using the package manager. You can bypass the version check and choose the unit file directory yourself by giving `--with-systemdsystemunitdir=DIR` or `-Dsystemdsystemunitdir=DIR` as an option to either `configure` or `meson`, respectively. ## Usage Manual pages: ```sh man 8 ksmbd.addshare man 8 ksmbd.adduser man 8 ksmbd.control man 8 ksmbd.mountd man 5 ksmbd.conf man 5 ksmbdpwd.db ``` Example session: ```sh # If you built and installed ksmbd-tools yourself using autoconf defaults, # the utilities are in `/usr/local/sbin', # the default user database is `/usr/local/etc/ksmbd/ksmbdpwd.db', and # the default configuration file is `/usr/local/etc/ksmbd/ksmbd.conf'. # Otherwise it is likely that, # the utilities are in `/usr/sbin', # the default user database is `/etc/ksmbd/ksmbdpwd.db', and # the default configuration file is `/etc/ksmbd/ksmbd.conf'. # Create the share path directory. # The share stores files in this directory using its underlying filesystem. mkdir -vp $HOME/MyShare # Add a share to the default configuration file. # Note that `ksmbd.addshare' does not do variable expansion. # Without `--add', `ksmbd.addshare' will update `MyShare' if it exists. sudo ksmbd.addshare --add \ --option "path = $HOME/MyShare" \ --option 'read only = no' \ MyShare # The default configuration file now has a new section for `MyShare'. # # [MyShare] # ; share parameters # path = /home/tester/MyShare # read only = no # # Each share has its own section with share parameters that apply to it. # A share parameter given in `[global]' changes its default value. # `[global]' also has global parameters which are not share specific. # You can interactively update a share by omitting `--option'. # Without `--update', `ksmbd.addshare' will add `MyShare' if it does not exist. sudo ksmbd.addshare --update MyShare # Add a user to the default user database. # You will be prompted for a password. sudo ksmbd.adduser --add MyUser # There is no system user called `MyUser' so it has to be mapped to one. # We can force all users accessing the share to map to a system user and group. # Update share parameters of a share in the default configuration file. sudo ksmbd.addshare --update \ --option "force user = $USER" \ --option "force group = $USER" \ MyShare # The default configuration file now has the updated share parameters. # # [MyShare] # ; share parameters # force group = tester # force user = tester # path = /home/tester/MyShare # read only = no # # Add the kernel module. sudo modprobe ksmbd # Start the user and kernel mode daemons. # All interfaces are listened to by default. sudo ksmbd.mountd # Mount the new share with cifs-utils and authenticate as the new user. # You will be prompted for the password given previously with `ksmbd.adduser'. sudo mount -o user=MyUser //127.0.0.1/MyShare /mnt # You can now access the share at `/mnt'. sudo touch /mnt/new_file_from_cifs_utils # Unmount the share. sudo umount /mnt # Update the password of a user in the default user database. # `--password' can be used to give the password instead of prompting. sudo ksmbd.adduser --update --password MyNewPassword MyUser # Delete a user from the default user database. sudo ksmbd.adduser --delete MyUser # The utilities notify ksmbd.mountd of changes by sending it the SIGHUP signal. # This can be done manually when changes are made without using the utilities. sudo ksmbd.control --reload # Toggle ksmbd debug printing of the `smb' component. sudo ksmbd.control --debug smb # Some changes require restarting the user and kernel mode daemons. # Modifying any global parameter is one example of such a change. # Restarting means starting `ksmbd.mountd' after shutting the daemons down. # Shutdown the user and kernel mode daemons. sudo ksmbd.control --shutdown # Remove the kernel module. sudo modprobe -r ksmbd ``` ## Packages The following packaging status tracker is provided by [the Repology project](https://repology.org) . [![Packaging status](https://repology.org/badge/vertical-allrepos/ksmbd-tools.svg)](https://repology.org/project/ksmbd-tools/versions) ksmbd-tools-3.5.2/addshare/000077500000000000000000000000001460410342700155335ustar00rootroot00000000000000ksmbd-tools-3.5.2/addshare/Makefile.am000066400000000000000000000011521460410342700175660ustar00rootroot00000000000000AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common noinst_LIBRARIES = libaddshare.a libaddshare_a_SOURCES = share_admin.c addshare.c share_admin.h EXTRA_DIST = ksmbd.addshare.8.in \ meson.build man_MANS = ksmbd.addshare.8 $(man_MANS): %: %.in; @$(in_script) $< >$@ CLEANFILES = $(man_MANS) install-exec-hook: uninstall-hook $(MKDIR_P) $(DESTDIR)$(sbindir) ( cd $(DESTDIR)$(sbindir) && \ $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.addshare ) uninstall-hook: -rm $(DESTDIR)$(sbindir)/ksmbd.addshare ksmbd-tools-3.5.2/addshare/addshare.c000066400000000000000000000101561460410342700174550ustar00rootroot00000000000000// 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 "tools.h" #include "management/share.h" #include "management/user.h" #include "linux/ksmbd_server.h" #include "share_admin.h" static void usage(int status) { printf( "Usage: ksmbd.addshare [-v] [-C CONF] [-P PWDDB] [-a | -u | -d] [-o OPT]... SHARE\n"); if (status != EXIT_SUCCESS) printf("Try `ksmbd.addshare --help' for more information.\n"); else printf( "\n" "If neither `-a', `-u', nor `-d' is given, either add or update SHARE.\n" "SHARE must be UTF-8 and [1, " STR(KSMBD_REQ_MAX_SHARE_NAME) ") bytes.\n" "SHARE is case-insensitive.\n" "\n" " -a, --add add SHARE to configuration file\n" " -u, --update update SHARE in configuration file\n" " -d, --delete delete SHARE from configuration file\n" " -o, --option=OPT use OPT as share parameter instead of prompting;\n" " this option can be given multiple times\n" " -C, --config=CONF use CONF as configuration file instead of\n" " `" PATH_SMBCONF "'\n" " -P, --pwddb=PWDDB use PWDDB as user database instead of\n" " `" PATH_PWDDB "'\n" " -v, --verbose be verbose\n" " -V, --version output version information and exit\n" " -h, --help display this help and exit\n" "\n" "See ksmbd.addshare(8) for more details.\n"); } static const struct option opts[] = { {"add", no_argument, NULL, 'a' }, {"update", no_argument, NULL, 'u' }, {"delete", no_argument, NULL, 'd' }, {"option", required_argument, NULL, 'o' }, {"config", required_argument, NULL, 'C' }, {"pwddb", required_argument, NULL, 'P' }, {"verbose", no_argument, NULL, 'v' }, {"version", no_argument, NULL, 'V' }, {"help", no_argument, NULL, 'h' }, {NULL, 0, NULL, 0 } }; int addshare_main(int argc, char **argv) { int ret = -EINVAL; g_autofree char *smbconf = NULL, *name = NULL, *pwddb = NULL; g_auto(GStrv) options = NULL; g_autoptr(GPtrArray) __options = g_ptr_array_new_with_free_func(g_free); command_fn *command = NULL; int c; while ((c = getopt_long(argc, argv, "audo:C:P:vVh", opts, NULL)) != EOF) switch (c) { case 'a': command = command_add_share; break; case 'u': command = command_update_share; break; case 'd': command = command_delete_share; break; case 'o': gptrarray_printf(__options, "%s", optarg); break; case 'C': g_free(smbconf); smbconf = g_strdup(optarg); break; case 'P': g_free(pwddb); pwddb = g_strdup(optarg); break; case 'v': set_log_level(PR_DEBUG); break; case 'V': ret = show_version(); goto out; case 'h': ret = 0; /* Fall through */ case '?': default: usage(ret ? EXIT_FAILURE : EXIT_SUCCESS); goto out; } options = gptrarray_to_strv(__options); __options = NULL; if (optind == argc - 1) { name = g_strdup(argv[optind]); } else { usage(ret ? EXIT_FAILURE : EXIT_SUCCESS); goto out; } if (!shm_share_name(name, strchr(name, 0x00))) { pr_err("Invalid share name `%s'\n", name); goto out; } if (!pwddb) pwddb = g_strdup(PATH_PWDDB); if (!smbconf) smbconf = g_strdup(PATH_SMBCONF); ret = load_config(pwddb, smbconf); if (ret) goto out; if (!command) { if (g_hash_table_lookup(parser.groups, name)) command = command_update_share; else command = command_add_share; } ret = command(smbconf, name, options); smbconf = name = (char *)(options = NULL); if (ret) goto out; if (cp_parse_lock()) { pr_info("Ignored lock file\n"); goto out; } if (kill(global_conf.pid, SIGHUP) < 0) { pr_debug("Can't send SIGHUP to PID %d: %m\n", global_conf.pid); goto out; } pr_info("Notified mountd\n"); out: remove_config(); return ret ? EXIT_FAILURE : EXIT_SUCCESS; } ksmbd-tools-3.5.2/addshare/ksmbd.addshare.8.in000066400000000000000000000051631460410342700211100ustar00rootroot00000000000000.TH KSMBD.ADDSHARE "8" "" "ksmbd-tools @ksmbd_tools_version@" "System Administration" .SH NAME ksmbd.addshare \- configure shares for ksmbd.conf of ksmbd.mountd .SH SYNOPSIS .B ksmbd.addshare [\-v] [\-C \fI\,CONF\/\fR] [\-P \fI\,PWDDB\/\fR] [\-a | \-u | \-d] [\-o \fI\,OPT\/\fR]... \fI\,SHARE\/\fR .SH DESCRIPTION \fBksmbd.addshare\fR configures shares for \fBksmbd.conf\fP(5) configuration file of \fBksmbd.mountd\fP(8) user mode daemon. \fBksmbd.addshare\fR modifies \fBksmbd.conf\fR such that its existing formatting is not retained. \fBksmbd.addshare\fR can parse \fBksmbdpwd.db\fP(5) user database so as to provide completions when prompting. \fBksmbd.addshare\fR notifies \fBksmbd.mountd\fR of changes if it had made any. \fBksmbd.addshare\fR will only make changes that do not require restarting \fBksmbd.mountd\fR and \fBksmbd\fR to take effect. .SH OPTIONS .PP If neither \fB\-a\fR, \fB\-u\fR, nor \fB\-d\fR is given, either add or update \fI\,SHARE\/\fR. \fI\,SHARE\/\fR must be UTF-8 and [1, 64) bytes. \" KSMBD_REQ_MAX_SHARE_NAME \fI\,SHARE\/\fR is case-insensitive. .TP \fB\-a\fR, \fB\-\-add\/\fR Add \fI\,SHARE\/\fR to configuration file. .TP \fB\-u\fR, \fB\-\-update\/\fR Update \fI\,SHARE\/\fR in configuration file. .TP \fB\-d\fR, \fB\-\-delete\/\fR Delete \fI\,SHARE\/\fR from configuration file. .TP \fB\-o\fR, \fB\-\-option\fR=\fI\,OPT\/\fR Use \fI\,OPT\/\fR as share parameter instead of prompting. This option can be given multiple times. .TP \fB\-C\fR, \fB\-\-config\fR=\fI\,CONF\/\fR Use \fI\,CONF\/\fR as configuration file instead of \fB@sysconfdir@/ksmbd/ksmbd.conf\fR. \" PATH_SMBCONF .TP \fB\-P\fR, \fB\-\-pwddb\fR=\fI\,PWDDB\/\fR Use \fI\,PWDDB\/\fR as user database instead of \fB@sysconfdir@/ksmbd/ksmbdpwd.db\fR. \" PATH_PWDDB .TP \fB\-v\fR, \fB\-\-verbose\fR Be verbose. .TP \fB\-V\fR, \fB\-\-version\fR Output version information and exit. .TP \fB\-h\fR, \fB\-\-help\fR Display this help and exit. .SH "EXIT STATUS" The exit status is 0 on success and 1 on failure. Failure to notify \fBksmbd.mountd\fR of changes has no effect on exit status. .SH COPYRIGHT Copyright \(co 2015-2022 ksmbd-tools contributors. License GPLv2: GNU GPL version 2 . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH "REPORTING BUGS" For bug reports, use the issue tracker at https://github.com/cifsd-team/ksmbd-tools/issues. .SH "SEE ALSO" .TP \fBConfiguration File\fR \fBksmbd.conf\fP(5) .TP \fBUser Database\fR \fBksmbdpwd.db\fP(5) .TP \fBUtilities\fR \fBksmbd.adduser\fP(8), \fBksmbd.control\fP(8), \fBksmbd.mountd\fP(8) ksmbd-tools-3.5.2/addshare/meson.build000066400000000000000000000011701460410342700176740ustar00rootroot00000000000000addshare_lib = static_library( 'addshare', 'share_admin.c', 'addshare.c', 'share_admin.h', include_directories: include_dirs, c_args: [ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')), '-DRUNSTATEDIR="@0@"'.format(runstatedir), ], dependencies: [ glib_dep, ], ) configure_file( input: 'ksmbd.addshare.8.in', output: 'ksmbd.addshare.8', install_dir: get_option('mandir') / 'man8', configuration: in_data, ) install_symlink( 'ksmbd.addshare', install_dir: get_option('sbindir'), pointing_to: get_option('prefix') / get_option('libexecdir') / 'ksmbd.tools', ) ksmbd-tools-3.5.2/addshare/share_admin.c000066400000000000000000000404511460410342700201550ustar00rootroot00000000000000// 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 #include #include #include #include #include #include #include /* * WARNING: * * This must match KSMBD_SHARE_CONF enum 1:1. * Each description should be well under 80 characters. */ static const char *__defconf_fmt[KSMBD_SHARE_CONF_MAX] = { /*0*/ "; net view and browse list description [%s]", "; directory given access [%s]", "; allow passwordless connections with guest accounts [%s]", "; name of additional guest account [%s]", "; users have read only access [%s]", /*5*/ "; visible in net view and browse list [%s]", "; users have read-write access [%s]", "; users have read-write access [%s]", "; support dos file attributes with xattr [%s]", "; issue oplocks for file open requests [%s]", /*10*/ "; octal bitmask and'd with mapped file perms [%s]", "; octal bitmask and'd with mapped dir perms [%s]", "; octal bitmask or'd with final file perms [%s]", "; octal bitmask or'd with final dir perms [%s]", "; system group to map all users [%s]", /*15*/ "; system user to map all users [%s]", "; files beginning with dot are hidden [%s]", "; list of the only users allowed to connect [%s]", "; list of users disallowed to connect [%s]", "; list of users restricted to read only access [%s]", /*20*/ "; list of users granted read-write access [%s]", "; list of users mapped to the superuser [%s]", "; list of the only hosts allowed to connect [%s]", "; list of hosts disallowed to connect [%s]", "; maximum number of simultaneous connections [%s]", /*25*/ "; files and dirs made inaccessible, e.g. /file1/dir1/ [%s]", "; files and dirs inherit ownership from parent dir [%s]", "; follow symlinks in the path directory [%s]", "; list of vfs objects to overload io ops with [%s]", "; users have read-write access [%s]", /*30*/ "; path lookup can cross mountpoints [%s]", }; static char **__get_options(GHashTable *kv, int is_global) { GPtrArray *options = g_ptr_array_new(); enum KSMBD_SHARE_CONF c; for (c = 0; c < KSMBD_SHARE_CONF_MAX; c++) { const char *k = KSMBD_SHARE_CONF[c], *v = NULL, *pre; if ((is_global && KSMBD_SHARE_CONF_IS_GLOBAL(c)) || KSMBD_SHARE_CONF_IS_BROKEN(c)) pre = "; "; else pre = ""; if (kv) v = g_hash_table_lookup(kv, k); if (!v) v = ""; gptrarray_printf(options, "%s%s = %s", pre, k, v); } return gptrarray_to_strv(options); } static void __load_conf(enum KSMBD_SHARE_CONF conf, int step, char **options, char *buf, size_t *buflen) { char *option = options[conf]; if (step) { if (cp_smbconf_eol(cp_ltrim(buf))) { char *p = cp_ltrim(strchr(option, '=') + 1); const char *fmt, *s; if (*p == 0x00) { fmt = __defconf_fmt[conf]; s = KSMBD_SHARE_DEFCONF[conf]; } else { fmt = "%s"; s = p; } snprintf(buf, LINE_MAX, fmt, s); *buflen = strlen(buf); *p = 0x00; } else { options[conf] = g_strdup_printf("%s%s", option, buf); } } printf("\r" "\e[2K" "%s%s" "\e[6n", option, buf); if (option != options[conf]) g_free(option); } static enum KSMBD_SHARE_CONF __next_conf(enum KSMBD_SHARE_CONF conf, int step, int is_global, int *is_ready) { if (conf > KSMBD_SHARE_CONF_MAX) return 0; if (conf == KSMBD_SHARE_CONF_MAX) { if (*is_ready) return conf; *is_ready = !*is_ready; printf("\eD" "\r" "\e[1m" "Ready to commit changes!" "\e[m"); return __next_conf(conf - 1, -1, is_global, is_ready); } if ((is_global && KSMBD_SHARE_CONF_IS_GLOBAL(conf)) || KSMBD_SHARE_CONF_IS_BROKEN(conf)) return __next_conf(conf + step, step, is_global, is_ready); if (step > 0) printf("\eD"); else if (step < 0) printf("\eM"); return conf; } static GList *clear_ml(GList *ml) { g_list_free_full(ml, g_free); return NULL; } static GList *next_ml(GList *ml) { GList *first = ml; ml = g_list_remove_link(ml, first); return g_list_concat(ml, first); } static GList *init_ml(GList *ml) { g_free(ml->data); return g_list_delete_link(ml, ml); } static void __new_user_ml_cb(struct ksmbd_user *user, GList **ml) { if (!strncmp(user->name, (*ml)->data, strlen((*ml)->data))) *ml = g_list_insert_sorted(*ml, g_strdup(user->name), (GCompareFunc)strcmp); } static GList *new_user_ml(GList *ml, char *p) { if (ml) return next_ml(ml); ml = g_list_append(ml, g_strdup(p)); usm_iter_users((user_cb)__new_user_ml_cb, &ml); return init_ml(ml); } static GList *new_system_group_ml(GList *ml, char *p) { struct group *e; if (ml) return next_ml(ml); ml = g_list_append(ml, g_strdup(p)); setgrent(); while ((e = getgrent())) if (!strncmp(e->gr_name, ml->data, strlen(ml->data))) ml = g_list_insert_sorted(ml, g_strdup(e->gr_name), (GCompareFunc)strcmp); endgrent(); return init_ml(ml); } static GList *new_system_user_ml(GList *ml, char *p) { struct passwd *e; if (ml) return next_ml(ml); ml = g_list_append(ml, g_strdup(p)); setpwent(); while ((e = getpwent())) if (!strncmp(e->pw_name, ml->data, strlen(ml->data))) ml = g_list_insert_sorted(ml, g_strdup(e->pw_name), (GCompareFunc)strcmp); endpwent(); return init_ml(ml); } static GList *new_path_ml(GList *ml, char *p, char *buf) { g_autofree char *name = NULL; DIR *dir; struct dirent *e; if (ml) return next_ml(ml); ml = g_list_append(ml, g_strdup(p)); for (name = buf; name < p; name++) if (*name == '/') break; name = g_strndup(name, p - name); if (*name == 0x00) return init_ml(ml); if (global_conf.root_dir) { char *new_name = g_strdup_printf("%s%s", global_conf.root_dir, name); g_free(name); name = new_name; } dir = opendir(name); if (!dir) return init_ml(ml); while ((e = readdir(dir))) if (e->d_type == DT_DIR && strcmp(e->d_name, ".") && strcmp(e->d_name, "..") && !strncmp(e->d_name, ml->data, strlen(ml->data))) ml = g_list_insert_sorted(ml, g_strdup(e->d_name), (GCompareFunc)strcmp); closedir(dir); return init_ml(ml); } static GList *new_va_ml(GList *ml, char *p, const char *arg, ...) { va_list args; if (ml) return next_ml(ml); ml = g_list_append(ml, g_strdup(p)); va_start(args, arg); for (; arg; arg = va_arg(args, char *)) if (!strncmp(arg, ml->data, strlen(ml->data))) ml = g_list_append(ml, g_strdup(arg)); va_end(args); return init_ml(ml); } static char *__rtrim_path(char *buf, char *p) { for (; p > buf; p--) if (p[-1] == '/') break; return p; } static char *__rtrim_list(char *buf, char *p) { for (; p > buf; p--) if (p[-1] == '\t' || p[-1] == ' ' || p[-1] == ',') break; return p; } static GList *new_conf_ml(GList *ml, enum KSMBD_SHARE_CONF conf, char *buf, size_t *buflen) { char *p = buf; switch (conf) { case KSMBD_SHARE_CONF_PATH: p = __rtrim_path(buf, p + *buflen); ml = new_path_ml(ml, p, buf); if (!*buflen) { buf[(*buflen)++] = '/'; buf[(*buflen)] = 0x00; ml = clear_ml(ml); } break; case KSMBD_SHARE_CONF_GUEST_OK: case KSMBD_SHARE_CONF_READ_ONLY: case KSMBD_SHARE_CONF_BROWSEABLE: case KSMBD_SHARE_CONF_WRITE_OK: case KSMBD_SHARE_CONF_WRITEABLE: case KSMBD_SHARE_CONF_STORE_DOS_ATTRIBUTES: case KSMBD_SHARE_CONF_OPLOCKS: case KSMBD_SHARE_CONF_HIDE_DOT_FILES: case KSMBD_SHARE_CONF_INHERIT_OWNER: case KSMBD_SHARE_CONF_FOLLOW_SYMLINKS: case KSMBD_SHARE_CONF_WRITABLE: case KSMBD_SHARE_CONF_CROSSMNT: ml = new_va_ml(ml, p, "yes", "no", NULL); break; case KSMBD_SHARE_CONF_GUEST_ACCOUNT: ml = new_user_ml(ml, p); break; case KSMBD_SHARE_CONF_FORCE_GROUP: ml = new_system_group_ml(ml, p); break; case KSMBD_SHARE_CONF_FORCE_USER: ml = new_system_user_ml(ml, p); break; case KSMBD_SHARE_CONF_VALID_USERS: case KSMBD_SHARE_CONF_INVALID_USERS: case KSMBD_SHARE_CONF_READ_LIST: case KSMBD_SHARE_CONF_WRITE_LIST: case KSMBD_SHARE_CONF_ADMIN_USERS: p = __rtrim_list(buf, p + *buflen); ml = new_user_ml(ml, p); break; case KSMBD_SHARE_CONF_VFS_OBJECTS: p = __rtrim_list(buf, p + *buflen); ml = new_va_ml(ml, p, "acl_xattr", "streams_xattr", NULL); } if (ml) { snprintf(p, LINE_MAX - (p - buf), "%s", (char *)ml->data); *buflen = p - buf + strlen(p); } return ml; } static int __prompt_options_stdin(char **options, int is_global) { g_autoptr(GList) ml = NULL; struct termios term, raw_term; enum KSMBD_SHARE_CONF conf; int is_ready, step, ccode, icode, licode, row, col; char buf[LINE_MAX]; size_t buflen; pr_info("Prompting for options\n"); tcgetattr(STDIN_FILENO, &term); raw_term = term; raw_term.c_lflag &= ~(ECHO | ICANON); tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term); printf( " next parameter\n" " previous parameter\n" " erase value\n" " cycle completions\n" " , list separator\n" " ; # comment with [default value]\n" "\e[1m" "Share parameters" "\e[m" "\n"); for (conf = is_ready = 0, step = 1; conf < KSMBD_SHARE_CONF_MAX; conf = __next_conf(conf + step, step, is_global, &is_ready)) { int c; if (step) { buflen = 0; buf[buflen] = 0x00; ml = clear_ml(ml); __load_conf(conf, step, options, buf, &buflen); ccode = icode = licode = row = col = 0; step = 0; } if (ccode == 'A') { step = -1; __load_conf(conf, step, options, buf, &buflen); ccode = icode = licode = 0; continue; } if (ccode == 'B') { step = 1; __load_conf(conf, step, options, buf, &buflen); ccode = icode = licode = 0; continue; } if (ccode >= '0' && ccode <= '9') { icode = 10 * icode + ccode - '0'; } else if (ccode == ';') { licode = icode; icode = 0; } else if (ccode == 'R') { col = icode; row = licode; ccode = icode = licode = 0; } c = getchar(); if (c == EOF || c == 0x04) break; if (c == '\e' || (c == '[' && ccode == '\e') || (c == 'A' && ccode == '[') || (c == 'B' && ccode == '[') || (c >= '0' && c <= '9' && ccode == '[') || (c >= '0' && c <= '9' && ccode >= '0' && ccode <= '9') || (c == ';' && ccode >= '0' && ccode <= '9') || (c >= '0' && c <= '9' && ccode == ';') || (c == 'R' && ccode >= '0' && ccode <= '9')) { ccode = c; continue; } ccode = icode = licode = 0; if (c == 0x10) { step = -1; __load_conf(conf, step, options, buf, &buflen); continue; } if (c == 0x0E || c == '\n') { step = 1; __load_conf(conf, step, options, buf, &buflen); continue; } if (c == 0x17) { buflen = 0; buf[buflen] = 0x00; ml = clear_ml(ml); __load_conf(conf, step, options, buf, &buflen); continue; } if (cp_smbconf_eol(cp_ltrim(buf))) { buflen = 0; buf[buflen] = 0x00; ml = clear_ml(ml); } if (c == '\t') { ml = new_conf_ml(ml, conf, buf, &buflen); __load_conf(conf, step, options, buf, &buflen); continue; } if (c == raw_term.c_cc[VERASE]) { char *u8c; size_t clen; u8c = g_utf8_find_prev_char(buf, buf + buflen); clen = u8c ? buf + buflen - u8c : 1; buflen -= buflen > clen ? clen : buflen; buf[buflen] = 0x00; ml = clear_ml(ml); __load_conf(conf, step, options, buf, &buflen); continue; } if (buflen < LINE_MAX - 1 && col < 80) { buf[buflen++] = c; buf[buflen] = 0x00; ml = clear_ml(ml); if (!cp_printable(buf + buflen - 1)) buf[--buflen] = 0x00; __load_conf(conf, step, options, buf, &buflen); } } printf("\eD" "\r" "\e[2K"); raw_term.c_cc[VMIN] = 0; raw_term.c_cc[VTIME] = 3; tcsetattr(STDIN_FILENO, TCSANOW, &raw_term); while (getchar() != EOF) ; tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); ml = clear_ml(ml); return conf < KSMBD_SHARE_CONF_MAX ? -EINVAL : 0; } static int process_options(char ***options, GHashTable *kv, int is_global) { if (!**options) { g_free(*options); *options = __get_options(kv, is_global); return __prompt_options_stdin(*options, is_global); } return 0; } static GList *new_share_nl(void) { GList *nl = g_hash_table_get_keys(parser.groups), *l = NULL; nl = g_list_sort(nl, (GCompareFunc)strcmp); if (parser.ipc) { l = g_list_find(nl, parser.ipc->name); nl = g_list_remove_link(nl, l); } nl = g_list_concat(l, nl); if (parser.global) { l = g_list_find(nl, parser.global->name); nl = g_list_remove_link(nl, l); } return g_list_concat(l, nl); } static GList *new_share_kl(struct smbconf_group *g) { GList *l = g_hash_table_get_keys(g->kv), *kl = NULL; int is_global = g == parser.global; enum KSMBD_SHARE_CONF c; char *k; for (c = 0; c < KSMBD_SHARE_CONF_MAX; c++) if ((!is_global || !KSMBD_SHARE_CONF_IS_GLOBAL(c)) && !KSMBD_SHARE_CONF_IS_BROKEN(c) && g_hash_table_lookup_extended(g->kv, KSMBD_SHARE_CONF[c], (gpointer *)&k, NULL)) { l = g_list_remove(l, k); kl = g_list_insert_sorted(kl, k, (GCompareFunc)strcmp); } l = g_list_sort(l, (GCompareFunc)strcmp); if (kl) kl = g_list_insert(kl, NULL, 0); return g_list_concat(l, kl); } static void __gptrarray_add_share_kl(GPtrArray *gptrarray, GList *kl, GHashTable *kv, int is_global) { GList *l; if (kl && kl->data) gptrarray_printf( gptrarray, "\t" "; " "%s" "parameters\n", is_global ? "global " : ""); for (l = kl; l; l = l->next) { char *k, *v; if (!l->data) { gptrarray_printf( gptrarray, "%s" "\t" "; " "%s" "share parameters\n", kl->data ? "\n" : "", is_global ? "default " : ""); continue; } k = l->data; v = g_hash_table_lookup(kv, k); gptrarray_printf(gptrarray, "\t" "%s = %s\n", k, v); } } static char *get_conf_contents(void) { GPtrArray *lines = g_ptr_array_new(); g_autoptr(GList) nl = new_share_nl(); GList *l; gptrarray_printf(lines, "; see ksmbd.conf(5) for details\n" "\n"); for (l = nl; l; l = l->next) { struct smbconf_group *g = g_hash_table_lookup(parser.groups, l->data); g_autoptr(GList) kl = new_share_kl(g); gptrarray_printf(lines, "[%s]\n", g->name); __gptrarray_add_share_kl(lines, kl, g->kv, g == parser.global); gptrarray_printf(lines, "\n"); } return gptrarray_to_str(lines); } int command_add_share(char *smbconf, char *name, char **options) { g_autofree char *contents = NULL; int ret, is_global; if (g_hash_table_lookup(parser.groups, name)) { pr_err("Share `%s' already exists\n", name); ret = -EEXIST; goto out; } is_global = shm_share_name_equal(name, "global"); ret = process_options(&options, NULL, is_global); if (ret) goto out; cp_parse_external_smbconf_group(name, options); contents = get_conf_contents(); ret = set_conf_contents(smbconf, contents); if (ret) goto out; pr_info("Added share `%s'\n", name); out: g_free(smbconf); g_free(name); g_strfreev(options); return ret; } int command_update_share(char *smbconf, char *name, char **options) { g_autofree char *contents = NULL; struct smbconf_group *g; int ret, is_global; g = g_hash_table_lookup(parser.groups, name); if (!g) { pr_err("Share `%s' does not exist\n", name); ret = -EINVAL; goto out; } is_global = g == parser.global; ret = process_options(&options, g->kv, is_global); if (ret) goto out; cp_parse_external_smbconf_group(name, options); contents = get_conf_contents(); ret = set_conf_contents(smbconf, contents); if (ret) goto out; pr_info("Updated share `%s'\n", name); out: g_free(smbconf); g_free(name); g_strfreev(options); return ret; } int command_delete_share(char *smbconf, char *name, char **options) { g_autofree char *contents = NULL; struct smbconf_group *g; int ret, is_global; g = g_hash_table_lookup(parser.groups, name); if (!g) { pr_err("Share `%s' does not exist\n", name); ret = -EINVAL; goto out; } is_global = g == parser.global; if (is_global) { g_strfreev(options); options = __get_options(NULL, is_global); return command_update_share(smbconf, name, options); } g_hash_table_remove(parser.groups, name); contents = get_conf_contents(); ret = set_conf_contents(smbconf, contents); if (ret) goto out; pr_info("Deleted share `%s'\n", name); out: g_free(smbconf); g_free(name); g_strfreev(options); return ret; } ksmbd-tools-3.5.2/addshare/share_admin.h000066400000000000000000000006231460410342700201570ustar00rootroot00000000000000/* 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__ typedef int command_fn(char *smbconf, char *name, char **options); command_fn command_add_share, command_update_share, command_delete_share; #endif /* __KSMBD_SHARE_ADMIN_H__ */ ksmbd-tools-3.5.2/adduser/000077500000000000000000000000001460410342700154075ustar00rootroot00000000000000ksmbd-tools-3.5.2/adduser/Makefile.am000066400000000000000000000011671460410342700174500ustar00rootroot00000000000000AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common noinst_LIBRARIES = libadduser.a libadduser_a_SOURCES = md4_hash.c user_admin.c adduser.c md4_hash.h user_admin.h EXTRA_DIST = ksmbd.adduser.8.in \ meson.build man_MANS = ksmbd.adduser.8 $(man_MANS): %: %.in; @$(in_script) $< >$@ CLEANFILES = $(man_MANS) install-exec-hook: uninstall-hook $(MKDIR_P) $(DESTDIR)$(sbindir) ( cd $(DESTDIR)$(sbindir) && \ $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.adduser ) uninstall-hook: -rm $(DESTDIR)$(sbindir)/ksmbd.adduser ksmbd-tools-3.5.2/adduser/adduser.c000066400000000000000000000100331460410342700171770ustar00rootroot00000000000000// 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 "tools.h" #include "management/user.h" #include "management/share.h" #include "user_admin.h" #include "linux/ksmbd_server.h" static void usage(int status) { printf( "Usage: ksmbd.adduser [-v] [-P PWDDB] [-c CONF] [-a | -u | -d] [-p PWD] USER\n"); if (status != EXIT_SUCCESS) printf("Try `ksmbd.adduser --help' for more information.\n"); else printf( "\n" "If neither `-a', `-u', nor `-d' is given, either add or update USER.\n" "USER must be UTF-8 and [1, " STR(KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) ") bytes.\n" "USER cannot contain colon (`:').\n" "\n" " -a, --add add USER to user database\n" " -u, --update update USER in user database\n" " -d, --delete delete USER from user database\n" " -p, --password=PWD use PWD as user password instead of prompting;\n" " PWD must be UTF-8 and [0, " STR(MAX_NT_PWD_LEN) ") bytes\n" " -P, --pwddb=PWDDB use PWDDB as user database instead of\n" " `" PATH_PWDDB "'\n" " -C, --config=CONF use CONF as configuration file instead of\n" " `" PATH_SMBCONF "'\n" " -v, --verbose be verbose\n" " -V, --version output version information and exit\n" " -h, --help display this help and exit\n" "\n" "See ksmbd.adduser(8) for more details.\n"); } static const struct option opts[] = { {"add", no_argument, NULL, 'a' }, {"delete", no_argument, NULL, 'd' }, {"update", no_argument, NULL, 'u' }, {"password", required_argument, NULL, 'p' }, {"pwddb", required_argument, NULL, 'P' }, {"config", required_argument, NULL, 'C' }, {"verbose", no_argument, NULL, 'v' }, {"version", no_argument, NULL, 'V' }, {"help", no_argument, NULL, 'h' }, {NULL, 0, NULL, 0 } }; int adduser_main(int argc, char **argv) { int ret = -EINVAL; g_autofree char *pwddb = NULL, *name = NULL, *password = NULL; g_autofree char *smbconf = NULL; command_fn *command = NULL; int c; while ((c = getopt_long(argc, argv, "audp:P:C:vVh", opts, NULL)) != EOF) switch (c) { case 'a': command = command_add_user; break; case 'u': command = command_update_user; break; case 'd': command = command_delete_user; break; case 'p': g_free(password); password = g_strdup(optarg); break; case 'P': g_free(pwddb); pwddb = g_strdup(optarg); break; case 'C': g_free(smbconf); smbconf = g_strdup(optarg); break; case 'v': set_log_level(PR_DEBUG); break; case 'V': ret = show_version(); goto out; case 'h': ret = 0; /* Fall through */ case '?': default: usage(ret ? EXIT_FAILURE : EXIT_SUCCESS); goto out; } if (optind == argc - 1) { name = g_strdup(argv[optind]); } else { usage(ret ? EXIT_FAILURE : EXIT_SUCCESS); goto out; } if (!usm_user_name(name, strchr(name, 0x00))) { pr_err("Invalid user name `%s'\n", name); goto out; } if (!pwddb) pwddb = g_strdup(PATH_PWDDB); if (!smbconf) smbconf = g_strdup(PATH_SMBCONF); ret = load_config(pwddb, smbconf); if (ret) goto out; if (!command) { struct ksmbd_user *user = usm_lookup_user(name); if (user) { put_ksmbd_user(user); command = command_update_user; } else { command = command_add_user; } } ret = command(pwddb, name, password); pwddb = name = password = NULL; if (ret) goto out; if (cp_parse_lock()) { pr_info("Ignored lock file\n"); goto out; } if (kill(global_conf.pid, SIGHUP) < 0) { pr_debug("Can't send SIGHUP to PID %d: %m\n", global_conf.pid); goto out; } pr_info("Notified mountd\n"); out: remove_config(); return ret ? EXIT_FAILURE : EXIT_SUCCESS; } ksmbd-tools-3.5.2/adduser/ksmbd.adduser.8.in000066400000000000000000000046571460410342700206470ustar00rootroot00000000000000.TH KSMBD.ADDUSER "8" "" "ksmbd-tools @ksmbd_tools_version@" "System Administration" .SH NAME ksmbd.adduser \- configure users for ksmbdpwd.db of ksmbd.mountd .SH SYNOPSIS .B ksmbd.adduser [\-v] [\-P \fI\,PWDDB\/\fR] [\-C \fI\,CONF\/\fR] [\-a | \-u | \-d] [\-p \fI\,PWD\/\fR] \fI\,USER\/\fR .SH DESCRIPTION \fBksmbd.adduser\fR configures users for \fBksmbdpwd.db\fP(5) user database of \fBksmbd.mountd\fP(8) user mode daemon. \fBksmbd.adduser\fR can parse \fBksmbd.conf\fP(5) configuration file so as to guard against deletion of users that are depended on. \fBksmbd.adduser\fR notifies \fBksmbd.mountd\fR of changes if it had made any. .SH OPTIONS .PP If neither \fB\-a\fR, \fB\-u\fR, nor \fB\-d\fR is given, either add or update \fI\,USER\/\fR. \fI\,USER\/\fR must be UTF-8 and [1, 48) bytes. \" KSMBD_REQ_MAX_ACCOUNT_NAME_SZ \fI\,USER\/\fR cannot contain colon (\fB:\fR). .TP \fB\-a\fR, \fB\-\-add\/\fR Add \fI\,USER\/\fR to user database. .TP \fB\-u\fR, \fB\-\-update\/\fR Update \fI\,USER\/\fR in user database. .TP \fB\-d\fR, \fB\-\-delete\/\fR Delete \fI\,USER\/\fR from user database. .TP \fB\-p\fR, \fB\-\-password\fR=\fI\,PWD\/\fR Use \fI\,PWD\/\fR as user password instead of prompting. \fI\,PWD\/\fR must be UTF-8 and [0, 129) bytes. \" MAX_NT_PWD_LEN .TP \fB\-P\fR, \fB\-\-pwddb\fR=\fI\,PWDDB\/\fR Use \fI\,PWDDB\/\fR as user database instead of \fB@sysconfdir@/ksmbd/ksmbdpwd.db\fR. \" PATH_PWDDB .TP \fB\-C\fR, \fB\-\-config\fR=\fI\,CONF\/\fR Use \fI\,CONF\/\fR as configuration file instead of \fB@sysconfdir@/ksmbd/ksmbd.conf\fR. \" PATH_SMBCONF .TP \fB\-v\fR, \fB\-\-verbose\fR Be verbose. .TP \fB\-V\fR, \fB\-\-version\fR Output version information and exit. .TP \fB\-h\fR, \fB\-\-help\fR Display this help and exit. .SH "EXIT STATUS" The exit status is 0 on success and 1 on failure. Failure to notify \fBksmbd.mountd\fR of changes has no effect on exit status. .SH COPYRIGHT Copyright \(co 2015-2022 ksmbd-tools contributors. License GPLv2: GNU GPL version 2 . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH "REPORTING BUGS" For bug reports, use the issue tracker at https://github.com/cifsd-team/ksmbd-tools/issues. .SH "SEE ALSO" .TP \fBUser Database\fR \fBksmbdpwd.db\fP(5) .TP \fBConfiguration File\fR \fBksmbd.conf\fP(5) .TP \fBUtilities\fR \fBksmbd.addshare\fP(8), \fBksmbd.control\fP(8), \fBksmbd.mountd\fP(8) ksmbd-tools-3.5.2/adduser/md4_hash.c000066400000000000000000000123531460410342700172460ustar00rootroot00000000000000// 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.5.2/adduser/md4_hash.h000066400000000000000000000021141460410342700172450ustar00rootroot00000000000000/* 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.5.2/adduser/meson.build000066400000000000000000000012201460410342700175440ustar00rootroot00000000000000adduser_lib = static_library( 'adduser', 'md4_hash.c', 'user_admin.c', 'adduser.c', 'md4_hash.h', 'user_admin.h', include_directories: include_dirs, c_args: [ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')), '-DRUNSTATEDIR="@0@"'.format(runstatedir), ], dependencies: [ glib_dep, ], ) configure_file( input: 'ksmbd.adduser.8.in', output: 'ksmbd.adduser.8', install_dir: get_option('mandir') / 'man8', configuration: in_data, ) install_symlink( 'ksmbd.adduser', install_dir: get_option('sbindir'), pointing_to: get_option('prefix') / get_option('libexecdir') / 'ksmbd.tools', ) ksmbd-tools-3.5.2/adduser/user_admin.c000066400000000000000000000160551460410342700177100ustar00rootroot00000000000000// 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 static void __prompt_password_stdin(char *password, size_t *sz) { struct termios term, raw_term; char buf[LINE_MAX]; size_t buflen; pr_info("Prompting for password\n"); tcgetattr(STDIN_FILENO, &term); raw_term = term; raw_term.c_lflag &= ~(ECHO | ICANON); tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term); for (*sz = (size_t)-1, buflen = 0, password[buflen] = buf[buflen] = 0x00;;) { int c; if (!buflen) printf("\r" "\e[2K" "%s password: ", *password == 0x00 ? "New" : "Retype"); c = getchar(); if (c == EOF || c == 0x04) break; if (c == '\n') { if (*password == 0x00 && buflen) { strncat(password, buf, MAX_NT_PWD_LEN - 1); buflen = 0; buf[buflen] = 0x00; printf("\eD"); continue; } if (strcmp(password, buf)) { *password = 0x00; buflen = 0; buf[buflen] = 0x00; printf("\r" "\e[2K" "\e[1m" "Passwords do not match!" "\e[m" "\eM"); continue; } if (!g_utf8_validate(password, -1, NULL)) { *password = 0x00; buflen = 0; buf[buflen] = 0x00; printf("\r" "\e[2K" "\e[1m" "Password is not UTF-8!" "\e[m" "\eM"); continue; } *sz = buflen + 1; break; } if (c == raw_term.c_cc[VERASE]) { char *u8c; size_t clen; u8c = g_utf8_find_prev_char(buf, buf + buflen); clen = u8c ? buf + buflen - u8c : 1; buflen -= buflen > clen ? clen : buflen; buf[buflen] = 0x00; continue; } if (buflen < LINE_MAX - 1) { buf[buflen++] = c; buf[buflen] = 0x00; if (!cp_printable(buf + buflen - 1)) buf[--buflen] = 0x00; } } printf("\eD" "\r" "\e[2K"); tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); } static int __is_valid_password_len(size_t len) { int is_valid; is_valid = len < MAX_NT_PWD_LEN; if (!is_valid) { pr_err("Password exceeds %d bytes\n", MAX_NT_PWD_LEN - 1); goto out; } if (!len) pr_info("Password is empty\n"); out: return is_valid; } static void __utf16le_convert(char **password, size_t *sz) { size_t bytes_written; char *utf16le; utf16le = ksmbd_gconvert(*password, *sz - 1, KSMBD_CHARSET_UTF16LE, KSMBD_CHARSET_DEFAULT, NULL, &bytes_written); g_free(*password); *sz = !utf16le ? (size_t)-1 : bytes_written; *password = utf16le; } static void __md4_hash(char **password, size_t *sz) { struct md4_ctx mctx; md4_init(&mctx); md4_update(&mctx, *password, *sz); g_free(*password); *sz = sizeof(mctx.hash) + 1; *password = g_malloc0(*sz); md4_final(&mctx, *password); } static void __base64_encode(char **password, size_t *sz) { char *base64; base64 = base64_encode(*password, *sz - 1); g_free(*password); *sz = strlen(base64) + 1; *password = base64; } static int process_password(char **password) { size_t sz; if (!*password) { *password = g_malloc(MAX_NT_PWD_LEN); __prompt_password_stdin(*password, &sz); if (sz == (size_t)-1) return -EINVAL; } else { sz = strlen(*password) + 1; } if (!__is_valid_password_len(sz - 1)) return -EINVAL; __utf16le_convert(password, &sz); if (sz == (size_t)-1) return -EINVAL; __md4_hash(password, &sz); __base64_encode(password, &sz); return 0; } static void __new_user_nl_cb(struct ksmbd_user *user, GList **nl) { if (!test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT)) *nl = g_list_insert_sorted(*nl, user->name, (GCompareFunc)strcmp); } static GList *new_user_nl(void) { GList *nl = NULL; usm_iter_users((user_cb)__new_user_nl_cb, &nl); return nl; } static char *get_conf_contents(void) { GPtrArray *lines = g_ptr_array_new(); g_autoptr(GList) nl = new_user_nl(); GList *l; for (l = nl; l; l = l->next) { struct ksmbd_user *user = usm_lookup_user(l->data); gptrarray_printf(lines, "%s:%s\n", user->name, user->pass_b64); put_ksmbd_user(user); } return gptrarray_to_str(lines); } int command_add_user(char *pwddb, char *name, char *password) { g_autofree char *contents = NULL; struct ksmbd_user *user; int ret; user = usm_lookup_user(name); if (user) { put_ksmbd_user(user); pr_err("User `%s' already exists\n", name); ret = -EEXIST; goto out; } ret = process_password(&password); if (ret) goto out; ret = usm_add_new_user(g_strdup(name), g_strdup(password)); if (ret) { pr_err("Failed to add user `%s'\n", name); goto out; } contents = get_conf_contents(); ret = set_conf_contents(pwddb, contents); if (ret) goto out; pr_info("Added user `%s'\n", name); out: g_free(pwddb); g_free(name); g_free(password); return ret; } int command_update_user(char *pwddb, char *name, char *password) { g_autofree char *contents = NULL; struct ksmbd_user *user; int ret; user = usm_lookup_user(name); if (!user) { pr_err("User `%s' does not exist\n", name); ret = -EINVAL; goto out; } ret = process_password(&password); if (ret) goto out; usm_update_user_password(user, password); put_ksmbd_user(user); contents = get_conf_contents(); ret = set_conf_contents(pwddb, contents); if (ret) goto out; pr_info("Updated user `%s'\n", name); out: g_free(pwddb); g_free(name); g_free(password); return ret; } static void __share_transient_user_cb(struct ksmbd_share *share, char **user_name) { if (!*user_name) return; if (share->guest_account && !strcmp(*user_name, share->guest_account)) goto require; if (!shm_lookup_users_map(share, KSMBD_SHARE_ADMIN_USERS_MAP, *user_name)) goto require; if (!shm_lookup_users_map(share, KSMBD_SHARE_WRITE_LIST_MAP, *user_name)) goto require; if (!shm_lookup_users_map(share, KSMBD_SHARE_VALID_USERS_MAP, *user_name)) goto require; return; require: pr_err("Share `%s' requires user `%s'\n", share->name, *user_name); *user_name = NULL; } static int __is_transient_user(char *name) { int is_transient; is_transient = !global_conf.guest_account || strcmp(global_conf.guest_account, name); if (!is_transient) { pr_err("Server requires user `%s'\n", name); goto out; } shm_iter_shares((share_cb)__share_transient_user_cb, &name); is_transient = !!name; out: return is_transient; } int command_delete_user(char *pwddb, char *name, char *password) { g_autofree char *contents = NULL; struct ksmbd_user *user; int ret; user = usm_lookup_user(name); if (!user) { pr_err("User `%s' does not exist\n", name); ret = -EINVAL; goto out; } if (!__is_transient_user(name)) { ret = -EINVAL; goto out; } usm_remove_user(user); contents = get_conf_contents(); ret = set_conf_contents(pwddb, contents); if (ret) goto out; pr_info("Deleted user `%s'\n", name); out: g_free(pwddb); g_free(name); g_free(password); return ret; } ksmbd-tools-3.5.2/adduser/user_admin.h000066400000000000000000000006471460410342700177150ustar00rootroot00000000000000/* 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__ #define MAX_NT_PWD_LEN 129 typedef int command_fn(char *pwddb, char *name, char *password); command_fn command_add_user, command_update_user, command_delete_user; #endif /* __KSMBD_USER_ADMIN_H__ */ ksmbd-tools-3.5.2/autogen.sh000077500000000000000000000000521460410342700157560ustar00rootroot00000000000000#!/bin/sh autoreconf --install --verbose ksmbd-tools-3.5.2/configure.ac000066400000000000000000000124061460410342700162510ustar00rootroot00000000000000AC_PREREQ([2.68]) m4_define([ksmbd_tools_version], m4_esyscmd_s([ exec awk '/define KSMBD_TOOLS_VERSION / { gsub(/"/,"",$3); printf "%s", $3 }' include/version.h ])) 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_SUBST([ksmbd_tools_version], ksmbd_tools_version) AC_LANG([C]) AC_PROG_CC AC_PROG_CC_STDC AM_SILENT_RULES([yes]) AC_PROG_LIBTOOL AC_PROG_SED AC_PROG_MKDIR_P AC_PROG_LN_S AC_SUBST([in_script], [[\ '$(SED) -e "s,[@]sbindir[@],$(sbindir),g" \ -e "s,[@]sysconfdir[@],$(sysconfdir),g" \ -e "s,[@]runstatedir[@],$(runstatedir),g" \ -e "s,[@]ksmbd_tools_version[@],$(ksmbd_tools_version),g"' ]]) AC_ARG_ENABLE([krb5], [AC_HELP_STRING([--enable-krb5], [Enable Kerberos 5 authentication @<:@default=no@:>@])], [enable_krb5=$enableval], [enable_krb5=no]) AS_IF([test "x$enable_krb5" != xno], [ PKG_CHECK_MODULES([LIBKRB5], [krb5]) save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $LIBKRB5_CFLAGS" AC_CHECK_HEADERS([krb5.h]) CPPFLAGS=$save_CPPFLAGS AS_IF([test "x$ac_cv_header_krb5_h" != xyes], [ AS_IF([test "x$enable_krb5" = xyes], [ AC_MSG_ERROR([krb5.h was not found.]) ], [ AC_MSG_WARN([krb5.h was not found, disabling Kerberos 5 support.]) enable_krb5=no ]) ]) ]) AC_ARG_WITH([rundir], [AC_HELP_STRING([--with-rundir=DIR], [Store modifiable per-process data in DIR @<:@LOCALSTATEDIR/run@:>@])], [with_rundir=$withval], [with_rundir=no]) AS_IF([test "x$with_rundir" = xno], [ AS_IF([test "x$runstatedir" = x], [ runstatedir='${localstatedir}/run' ], []) ], [ runstatedir=$with_rundir ]) AC_SUBST([runstatedir]) AC_ARG_WITH([systemdsystemunitdir], [AC_HELP_STRING([--with-systemdsystemunitdir@<:@=DIR@:>@], [Install systemd unit file to DIR (query pkg-config by default)])], [with_systemdsystemunitdir=$withval], [with_systemdsystemunitdir=yes]) AS_IF([test "x$with_systemdsystemunitdir" != xno], [ AS_IF([test "x$with_systemdsystemunitdir" = xyes], [ PKG_CHECK_VAR([systemdsystemunitdir], [systemd >= 245], [systemdsystemunitdir], [], []) ], [ systemdsystemunitdir=$with_systemdsystemunitdir ]) ]) AC_SUBST([systemdsystemunitdir]) save_LIBS=$LIBS LIBS= AC_SEARCH_LIBS([pthread_sigmask], [pthread], [], [ AC_MSG_ERROR([pthread was not found.]) ]) PTHREAD_LIBS=$LIBS LIBS=$save_LIBS AC_SUBST([PTHREAD_LIBS]) PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.44], [have_glib=yes], [have_glib=no]) AS_IF([test "x$have_glib" != xyes], [ AC_MSG_ERROR([glib (libglib2.0-dev or glib2-devel) was not found.]) ]) PKG_CHECK_MODULES([LIBNL], [libnl-3.0 >= 3.0 libnl-genl-3.0 >= 3.0], [have_libnl=yes], [have_libnl=no]) AS_IF([test "x$have_libnl" != xyes], [ AC_MSG_ERROR([libnl (libnl-3-dev or libnl3-devel) and libnl-genl (libnl-genl-3-dev) were not found.]) ]) AS_IF([test "x$enable_krb5" != xno], [ AC_DEFINE([CONFIG_KRB5], [], [Define if Kerberos 5 authentication is supported.]) save_LIBS=$LIBS LIBS="$LIBS $LIBKRB5_LIBS" AC_CHECK_FUNCS([krb5_auth_con_getrecvsubkey]) LIBS=$save_LIBS save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $LIBKRB5_CFLAGS" AC_CHECK_MEMBER([krb5_keyblock.keyvalue], [ac_cv_have_krb5_keyblock_keyvalue=yes], [ac_cv_have_krb5_keyblock_keyvalue=no], [[#include ]]) AC_CHECK_MEMBER([krb5_authenticator.client], [ac_cv_have_krb5_authenticator_client=yes], [ac_cv_have_krb5_authenticator_client=no], [[#include ]]) AC_CACHE_CHECK([for krb5_authenticator** parameter in krb5_auth_con_getauthenticator], [ac_cv_have_krb5_auth_con_getauthenticator_double_pointer], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include krb5_error_code krb5_auth_con_getauthenticator(krb5_context, krb5_auth_context, krb5_authenticator**); ]], [])], [ac_cv_have_krb5_auth_con_getauthenticator_double_pointer=yes], [ac_cv_have_krb5_auth_con_getauthenticator_double_pointer=no])]) CPPFLAGS=$save_CPPFLAGS AS_IF([test "x$ac_cv_have_krb5_keyblock_keyvalue" = xyes], [ AC_DEFINE([HAVE_KRB5_KEYBLOCK_KEYVALUE], [], [Define if krb5_keyblock has keyvalue member.]) ]) AS_IF([test "x$ac_cv_have_krb5_authenticator_client" = xyes], [ AC_DEFINE([HAVE_KRB5_AUTHENTICATOR_CLIENT], [], [Define if krb5_authenticator has client member.]) ]) AS_IF([test "x$ac_cv_have_krb5_auth_con_getauthenticator_double_pointer" = xyes], [ AC_DEFINE([HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER], [], [Define if krb5_auth_con_getauthenticator has krb5_authenticator** parameter.]) ]) ]) AM_CONDITIONAL(HAVE_LIBKRB5, [test "x$enable_krb5" != xno]) AC_CONFIG_FILES([ Makefile addshare/Makefile adduser/Makefile control/Makefile mountd/Makefile tools/Makefile ]) AC_OUTPUT ksmbd-tools-3.5.2/control/000077500000000000000000000000001460410342700154405ustar00rootroot00000000000000ksmbd-tools-3.5.2/control/Makefile.am000066400000000000000000000011071460410342700174730ustar00rootroot00000000000000AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common noinst_LIBRARIES = libcontrol.a libcontrol_a_SOURCES = control.c EXTRA_DIST = ksmbd.control.8.in \ meson.build man_MANS = ksmbd.control.8 $(man_MANS): %: %.in; @$(in_script) $< >$@ CLEANFILES = $(man_MANS) install-exec-hook: uninstall-hook $(MKDIR_P) $(DESTDIR)$(sbindir) ( cd $(DESTDIR)$(sbindir) && \ $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.control ) uninstall-hook: -rm $(DESTDIR)$(sbindir)/ksmbd.control ksmbd-tools-3.5.2/control/control.c000066400000000000000000000161041460410342700172660ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #define _GNU_SOURCE #include #include #include #include #include #include #include "tools.h" #include "config_parser.h" #define PATH_CLASS_ATTR_KILL_SERVER "/sys/class/ksmbd-control/kill_server" #define PATH_CLASS_ATTR_DEBUG "/sys/class/ksmbd-control/debug" #define PATH_MODULE_VERSION "/sys/module/ksmbd/version" static void usage(int status) { printf( "Usage: ksmbd.control [-v] -s\n" " ksmbd.control [-v] -r\n" " ksmbd.control [-v] -l\n" " ksmbd.control [-v] -d COMPONENT\n" " ksmbd.control [-v] -c\n"); if (status != EXIT_SUCCESS) printf("Try `ksmbd.control --help' for more information.\n"); else printf( "\n" " -s, --shutdown shutdown both ksmbd.mountd and ksmbd and exit\n" " -r, --reload notify ksmbd.mountd of changes and exit\n" " -l, --list list ksmbd.mountd shares and exit\n" " -d, --debug=COMPONENT toggle ksmbd debug printing for COMPONENT and exit;\n" " COMPONENT is `all', `smb', `auth', `vfs', `oplock',\n" " `ipc', `conn', or `rdma';\n" " enabled ones are output enclosed in brackets (`[]')\n" " -c, --ksmbd-version output ksmbd version information and exit\n" " -v, --verbose be verbose\n" " -V, --version output version information and exit\n" " -h, --help display this help and exit\n" "\n" "See ksmbd.control(8) for more details.\n"); } static const struct option opts[] = { {"shutdown", no_argument, NULL, 's' }, {"reload", no_argument, NULL, 'r' }, {"list", no_argument, NULL, 'l' }, {"debug", required_argument, NULL, 'd' }, {"ksmbd-version", no_argument, NULL, 'c' }, {"verbose", no_argument, NULL, 'v' }, {"version", no_argument, NULL, 'V' }, {"help", no_argument, NULL, 'h' }, {NULL, 0, NULL, 0 } }; static int control_shutdown(void) { int ret, fd; ret = cp_parse_lock(); if (!ret && kill(global_conf.pid, SIGTERM) < 0) { ret = -errno; pr_debug("Can't send SIGTERM to PID %d: %m\n", global_conf.pid); } if (ret) pr_err("Can't terminate mountd\n"); else pr_info("Terminated mountd\n"); fd = open(PATH_CLASS_ATTR_KILL_SERVER, O_WRONLY); if (fd < 0) { ret = -errno; pr_debug("Can't open `%s': %m\n", PATH_CLASS_ATTR_KILL_SERVER); goto err_kill; } if (write(fd, "hard", sizeof("hard") - 1) < 0) { ret = -errno; pr_debug("Can't write `%s': %m\n", PATH_CLASS_ATTR_KILL_SERVER); close(fd); goto err_kill; } close(fd); pr_info("Killed ksmbd\n"); return ret; err_kill: pr_err("Can't kill ksmbd\n"); return ret; } static int control_reload(void) { int ret; ret = cp_parse_lock(); if (!ret && kill(global_conf.pid, SIGHUP) < 0) { ret = -errno; pr_debug("Can't send SIGHUP to PID %d: %m\n", global_conf.pid); } if (ret) pr_err("Can't notify mountd\n"); else pr_info("Notified mountd\n"); return ret; } static int control_list(void) { g_autofree char *fifo_path = g_strdup_printf("%s.%d", PATH_FIFO, getpid()); int ret, fd; sigset_t sigset; if (mkfifo(fifo_path, S_IRUSR | S_IWUSR) < 0) { ret = -errno; pr_debug("Can't create `%s': %m\n", fifo_path); goto out; } fd = open(fifo_path, O_RDONLY | O_NONBLOCK); if (fd < 0) { ret = -errno; pr_debug("Can't open `%s': %m\n", fifo_path); goto out_unlink; } if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC) < 0 || fcntl(fd, F_SETOWN, getpid()) < 0) { ret = -errno; pr_debug("Can't control `%s': %m\n", fifo_path); goto out_close; } ret = cp_parse_lock(); if (ret) goto out_close; sigemptyset(&sigset); sigaddset(&sigset, SIGIO); sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGQUIT); sigaddset(&sigset, SIGTERM); pthread_sigmask(SIG_BLOCK, &sigset, NULL); if (kill(global_conf.pid, SIGUSR1) < 0) { ret = -errno; pr_debug("Can't send SIGUSR1 to PID %d: %m\n", global_conf.pid); goto out_close; } for (;;) { siginfo_t siginfo; if (sigwaitinfo(&sigset, &siginfo) < 0) continue; if (siginfo.si_signo != SIGIO) goto out_close; for (;;) { int bytes_read = splice(fd, NULL, STDOUT_FILENO, NULL, PIPE_BUF, 0); if (bytes_read < 0) { if (errno == EAGAIN) break; ret = -errno; pr_debug("Can't splice pipe: %m\n"); goto out_close; } if (!bytes_read) goto out_close; } } out_close: close(fd); out_unlink: unlink(fifo_path); out: if (ret) pr_err("Can't list mountd shares\n"); else pr_info("Listed mountd shares\n"); return ret; } static int control_show_version(void) { g_autofree char *version = NULL; int fd, ret; off_t len; fd = open(PATH_MODULE_VERSION, O_RDONLY); if (fd < 0) { ret = -errno; pr_debug("Can't open `%s': %m\n", PATH_MODULE_VERSION); goto err; } len = lseek(fd, 0, SEEK_END); if (len == (off_t)-1 || lseek(fd, 0, SEEK_SET) == (off_t)-1) { ret = -errno; pr_debug("Can't seek `%s': %m\n", PATH_MODULE_VERSION); close(fd); goto err; } version = g_malloc0(len + 1); if (read(fd, version, len) < 0) { ret = -errno; pr_debug("Can't read `%s': %m\n", PATH_MODULE_VERSION); close(fd); goto err; } ret = 0; close(fd); pr_info("ksmbd version : " "%s", version); return ret; err: pr_err("Can't output ksmbd version\n"); return ret; } static int control_debug(char *comp) { g_autofree char *debug = NULL; int fd, ret; off_t len; fd = open(PATH_CLASS_ATTR_DEBUG, O_RDWR); if (fd < 0) { ret = -errno; pr_debug("Can't open `%s': %m\n", PATH_CLASS_ATTR_DEBUG); goto err; } if (write(fd, comp, strlen(comp)) < 0) { ret = -errno; pr_debug("Can't write `%s': %m\n", PATH_CLASS_ATTR_DEBUG); close(fd); goto err; } len = lseek(fd, 0, SEEK_END); if (len == (off_t)-1 || lseek(fd, 0, SEEK_SET) == (off_t)-1) { ret = -errno; pr_debug("Can't seek `%s': %m\n", PATH_CLASS_ATTR_DEBUG); close(fd); goto err; } debug = g_malloc0(len + 1); if (read(fd, debug, len) < 0) { ret = -errno; pr_debug("Can't read `%s': %m\n", PATH_CLASS_ATTR_DEBUG); close(fd); goto err; } ret = 0; close(fd); pr_info("%s", debug); return ret; err: pr_err("Can't toggle ksmbd debug component\n"); return ret; } int control_main(int argc, char **argv) { int ret = -EINVAL; int c; while ((c = getopt_long(argc, argv, "srld:cvVh", opts, NULL)) != EOF) switch (c) { case 's': ret = control_shutdown(); goto out; case 'r': ret = control_reload(); goto out; case 'l': ret = control_list(); goto out; case 'd': ret = control_debug(optarg); goto out; case 'c': ret = control_show_version(); goto out; case 'v': set_log_level(PR_DEBUG); break; case 'V': ret = show_version(); goto out; case 'h': ret = 0; /* Fall through */ case '?': default: usage(ret ? EXIT_FAILURE : EXIT_SUCCESS); goto out; } usage(ret ? EXIT_FAILURE : EXIT_SUCCESS); out: return ret ? EXIT_FAILURE : EXIT_SUCCESS; } ksmbd-tools-3.5.2/control/ksmbd.control.8.in000066400000000000000000000034471460410342700207250ustar00rootroot00000000000000.TH KSMBD.CONTROL "8" "" "ksmbd-tools @ksmbd_tools_version@" "System Administration" .SH NAME ksmbd.control \- control ksmbd.mountd and ksmbd daemons .SH SYNOPSIS .B ksmbd.control [\-v] \-s .br .B ksmbd.control [\-v] \-r .br .B ksmbd.control [\-v] \-l .br .B ksmbd.control [\-v] \-d \fI\,COMPONENT\/\fR .br .B ksmbd.control [\-v] \-c .SH DESCRIPTION \fBksmbd.control\fR controls \fBksmbd.mountd\fP(8) user mode and \fBksmbd\fR kernel mode daemons. .SH OPTIONS .TP \fB\-s\fR, \fB\-\-shutdown\fR Shutdown both \fBksmbd.mountd\fR and \fBksmbd\fR and exit. .TP \fB\-r\fR, \fB\-\-reload\fR Notify \fBksmbd.mountd\fR of changes and exit. .TP \fB\-l\fR, \fB\-\-list\fR List \fBksmbd.mountd\fR shares and exit. .TP \fB\-d\fR, \fB\-\-debug\fR=\fI\,COMPONENT\/\fR Toggle \fBksmbd\fR debug printing for \fI\,COMPONENT\/\fR and exit. \fI\,COMPONENT\/\fR is \fBall\fR, \fBsmb\fR, \fBauth\fR, \fBvfs\fR, \fBoplock\fR, \fBipc\fR, \fBconn\fR, or \fBrdma\fR. Enabled ones are output enclosed in brackets (\fB[]\fR). .TP \fB\-c\fR, \fB\-\-ksmbd-version\fR Output \fBksmbd\fR version information and exit. .TP \fB\-v\fR, \fB\-\-verbose\fR Be verbose. .TP \fB\-V\fR, \fB\-\-version\fR Output version information and exit. .TP \fB\-h\fR, \fB\-\-help\fR Display this help and exit. .SH "EXIT STATUS" The exit status is 0 on success and 1 on failure. .SH COPYRIGHT Copyright \(co 2015-2022 ksmbd-tools contributors. License GPLv2: GNU GPL version 2 . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH "REPORTING BUGS" For bug reports, use the issue tracker at https://github.com/cifsd-team/ksmbd-tools/issues. .SH "SEE ALSO" .TP \fBUtilities\fR \fBksmbd.addshare\fP(8), \fBksmbd.adduser\fP(8), \fBksmbd.mountd\fP(8) ksmbd-tools-3.5.2/control/meson.build000066400000000000000000000011141460410342700175770ustar00rootroot00000000000000control_lib = static_library( 'control', 'control.c', include_directories: include_dirs, c_args: [ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')), '-DRUNSTATEDIR="@0@"'.format(runstatedir), ], dependencies: [ glib_dep, ], ) configure_file( input: 'ksmbd.control.8.in', output: 'ksmbd.control.8', install_dir: get_option('mandir') / 'man8', configuration: in_data, ) install_symlink( 'ksmbd.control', install_dir: get_option('sbindir'), pointing_to: get_option('prefix') / get_option('libexecdir') / 'ksmbd.tools', ) ksmbd-tools-3.5.2/include/000077500000000000000000000000001460410342700154035ustar00rootroot00000000000000ksmbd-tools-3.5.2/include/asn1.h000066400000000000000000000060161460410342700164210ustar00rootroot00000000000000/* 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.5.2/include/config_parser.h000066400000000000000000000027031460410342700203770ustar00rootroot00000000000000/* 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, *global, *ipc; }; extern struct smbconf_parser parser; static inline int cp_printable(unsigned char *p) { /* eighth bit is ok due to utf-8 mb */ return *p >= 0x20 && *p != 0x7F; } static inline int cp_smbconf_eol(char *p) { return *p == 0x00 || *p == ';' || *p == '#'; } static inline int cp_pwddb_eol(char *p) { return *p == 0x00; } void cp_parse_external_smbconf_group(char *name, char **options); void cp_smbconf_parser_init(void); void cp_smbconf_parser_destroy(void); int cp_parse_smbconf(char *smbconf); int cp_parse_pwddb(char *pwddb); int cp_parse_subauth(void); int cp_parse_lock(void); unsigned long long cp_memparse(char *v); char *cp_ltrim(const char *v); char *cp_rtrim(const char *v, const char *p); int cp_key_cmp(const char *lk, const char *rk); 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.5.2/include/ipc.h000066400000000000000000000017111460410342700163270ustar00rootroot00000000000000/* 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) /* * The netlink socket's receive buffer size needs to be increased * to avoid -ENOBUFS errors when receiving. */ #define KSMBD_IPC_SO_RCVBUF_SIZE (1 * 1024 * 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) 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); void ipc_init(void); #endif /* __KSMBD_IPC_H__ */ ksmbd-tools-3.5.2/include/linux/000077500000000000000000000000001460410342700165425ustar00rootroot00000000000000ksmbd-tools-3.5.2/include/linux/ksmbd_server.h000066400000000000000000000174041460410342700214070ustar00rootroot00000000000000/* 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) #define KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION_OFF (1 << 3) #define KSMBD_GLOBAL_FLAG_DURABLE_HANDLES (1 << 4) 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 smbd_max_io_size; /* smbd read write size */ __u32 max_connections; /* Number of maximum simultaneous connections */ __u32 reserved[126]; /* 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; __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; __u32 reserved[111]; /* Reserved room */ __u32 payload_sz; __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) #define KSMBD_SHARE_FLAG_UPDATE (1 << 14) #define KSMBD_SHARE_FLAG_CROSSMNT (1 << 15) /* * 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) #define KSMBD_TREE_CONN_FLAG_UPDATE (1 << 4) /* * 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.5.2/include/management/000077500000000000000000000000001460410342700175175ustar00rootroot00000000000000ksmbd-tools-3.5.2/include/management/session.h000066400000000000000000000014511460410342700213540ustar00rootroot00000000000000/* 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); void sm_init(void); #endif /* __MANAGEMENT_TCONNECTION_H__ */ ksmbd-tools-3.5.2/include/management/share.h000066400000000000000000000111611460410342700207720ustar00rootroot00000000000000/* 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_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_CROSSMNT = 30, KSMBD_SHARE_CONF_MAX }; extern const char *KSMBD_SHARE_CONF[KSMBD_SHARE_CONF_MAX]; extern const char *KSMBD_SHARE_DEFCONF[KSMBD_SHARE_CONF_MAX]; #define KSMBD_SHARE_CONF_IS_GLOBAL(c) \ ((c) == KSMBD_SHARE_CONF_GUEST_ACCOUNT || \ (c) == KSMBD_SHARE_CONF_MAX_CONNECTIONS) #define KSMBD_SHARE_CONF_IS_BROKEN(c) \ ((c) == KSMBD_SHARE_CONF_ADMIN_USERS || \ (c) == KSMBD_SHARE_CONF_HOSTS_ALLOW || \ (c) == KSMBD_SHARE_CONF_HOSTS_DENY || \ (c) == KSMBD_SHARE_CONF_FOLLOW_SYMLINKS) int shm_share_name(char *name, char *p); int shm_share_config(const 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); unsigned int shm_share_name_hash(const char *name); int shm_share_name_equal(const char *lname, const char *rname); void 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 (*share_cb)(struct ksmbd_share *share, void *data); void shm_iter_shares(share_cb cb, void *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.5.2/include/management/spnego.h000066400000000000000000000014741460410342700211710ustar00rootroot00000000000000/* 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 void 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 inline void spnego_init(void) {} static inline void spnego_destroy(void) {} static inline 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.5.2/include/management/tree_conn.h000066400000000000000000000020521460410342700216430ustar00rootroot00000000000000/* 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.5.2/include/management/user.h000066400000000000000000000030321460410342700206440ustar00rootroot00000000000000/* 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; } int usm_remove_user(struct ksmbd_user *user); 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); void usm_update_user_password(struct ksmbd_user *user, char *pass); int usm_user_name(char *name, char *p); int usm_add_new_user(char *name, char *pwd); int usm_add_guest_account(char *name); void usm_remove_all_users(void); void usm_destroy(void); void usm_init(void); typedef void (*user_cb)(struct ksmbd_user *user, void *data); void usm_iter_users(user_cb cb, void *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.5.2/include/rpc.h000066400000000000000000000241771460410342700163530ustar00rootroot00000000000000/* 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; GPtrArray *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); }; int ndr_read_int8(struct ksmbd_dcerpc *dce, __u8 *value); int ndr_read_int16(struct ksmbd_dcerpc *dce, __u16 *value); int ndr_read_int32(struct ksmbd_dcerpc *dce, __u32 *value); int ndr_read_int64(struct ksmbd_dcerpc *dce, __u64 *value); 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); int ndr_read_union_int32(struct ksmbd_dcerpc *dce, __u32 *value); 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); int ndr_read_vstring_ptr(struct ksmbd_dcerpc *dce, struct ndr_char_ptr *ctr); int ndr_read_uniq_vstring_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_vstring_ptr(struct ndr_uniq_char_ptr *ctr); int ndr_read_ptr(struct ksmbd_dcerpc *dce, struct ndr_ptr *ctr); int 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); void 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.5.2/include/rpc_lsarpc.h000066400000000000000000000015321460410342700177050ustar00rootroot00000000000000/* 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 #define DOMAIN_STR_SIZE 257 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[DOMAIN_STR_SIZE]; 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); void rpc_lsarpc_init(void); void rpc_lsarpc_destroy(void); #endif /* __KSMBD_RPC_LSARPC_H__ */ ksmbd-tools-3.5.2/include/rpc_samr.h000066400000000000000000000012701460410342700173620ustar00rootroot00000000000000/* 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); void rpc_samr_init(void); void rpc_samr_destroy(void); #endif /* __KSMBD_RPC_SAMR_H__ */ ksmbd-tools-3.5.2/include/rpc_srvsvc.h000066400000000000000000000007511460410342700177510ustar00rootroot00000000000000/* 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.5.2/include/rpc_wkssvc.h000066400000000000000000000007511460410342700177430ustar00rootroot00000000000000/* 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.5.2/include/smbacl.h000066400000000000000000000043651460410342700170250ustar00rootroot00000000000000/* 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); int smb_read_sid(struct ksmbd_dcerpc *dce, struct smb_sid *sid); int 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, size_t domain_len, int *type); #endif /* __KSMBD_SMBACL_H__ */ ksmbd-tools-3.5.2/include/tools.h000066400000000000000000000143321460410342700167170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #ifndef __TOOLS_H__ #define __TOOLS_H__ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #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 smbd_max_io_size; unsigned int max_connections; unsigned int share_fake_fscaps; unsigned int gen_subauth[3]; char *krb5_keytab_file; char *krb5_service_name; char *pwddb; char *smbconf; pid_t pid; }; #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_MAX_OPEN_FILES 65536 /* TODO */ #define KSMBD_CONF_MAX_ACTIVE_SESSIONS 65536 /* TODO */ #define KSMBD_CONF_MAX_CONNECTIONS 65536 #define PATH_PWDDB SYSCONFDIR "/ksmbd/ksmbdpwd.db" #define PATH_SMBCONF SYSCONFDIR "/ksmbd/ksmbd.conf" #define PATH_SUBAUTH SYSCONFDIR "/ksmbd/ksmbd.subauth" #define PATH_LOCK RUNSTATEDIR "/ksmbd.lock" #define PATH_FIFO RUNSTATEDIR "/ksmbd.fifo" #define KSMBD_HEALTH_START (0) #define KSMBD_HEALTH_RUNNING (1 << 0) #define KSMBD_SHOULD_RELOAD_CONFIG (1 << 1) #define KSMBD_SHOULD_LIST_CONFIG (1 << 2) extern int ksmbd_health_status; #define TRACING_DUMP_NL_MSG 0 #define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) //---------------------------------------------------------------// #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 extern int log_level; #define PR_LOGGER_STDIO 0 #define PR_LOGGER_SYSLOG 1 G_GNUC_PRINTF(2, 3) extern void __pr_log(int level, const char *fmt, ...); extern void pr_logger_init(int flags); extern int set_log_level(int level); #define pr_log(l, f, ...) \ do { \ if ((l) <= log_level) \ __pr_log((l), \ (f), \ get_tool_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]; char **gptrarray_to_strv(GPtrArray *gptrarray); char *gptrarray_to_str(GPtrArray *gptrarray); void gptrarray_printf(GPtrArray *gptrarray, const char *fmt, ...); int set_conf_contents(const char *conf, const char *contents); int load_config(char *pwddb, char *smbconf); void remove_config(void); extern int set_tool_main(char *name); extern const char *get_tool_name(void); int show_version(void); typedef int tool_main_fn(int argc, char **argv); tool_main_fn addshare_main, adduser_main, control_main, mountd_main; extern tool_main_fn *tool_main; #define TOOL_IS_ADDSHARE \ (tool_main == addshare_main) #define TOOL_IS_ADDUSER \ (tool_main == adduser_main) #define TOOL_IS_CONTROL \ (tool_main == control_main) #define TOOL_IS_MOUNTD \ (tool_main == mountd_main) #define SELECT_NAME(_1, _2, _3, _4, NAME, ...) NAME #define ghash_for_each_3(v, tbl, iter) \ for (g_hash_table_iter_init(&iter, tbl); \ g_hash_table_iter_next(&iter, NULL, (gpointer *)&v); \ ) #define ghash_for_each_4(k, v, tbl, iter) \ for (g_hash_table_iter_init(&iter, tbl); \ g_hash_table_iter_next(&iter, (gpointer *)&k, (gpointer *)&v); \ ) #define ghash_for_each(...) \ SELECT_NAME(__VA_ARGS__, \ ghash_for_each_4, \ ghash_for_each_3)(__VA_ARGS__) #define ghash_for_each_remove_3(v, tbl, iter) \ for (g_hash_table_iter_init(&iter, tbl); \ g_hash_table_iter_next(&iter, NULL, (gpointer *)&v); \ g_hash_table_iter_remove(&iter)) #define ghash_for_each_remove_4(k, v, tbl, iter) \ for (g_hash_table_iter_init(&iter, tbl); \ g_hash_table_iter_next(&iter, (gpointer *)&k, (gpointer *)&v); \ g_hash_table_iter_remove(&iter)) #define ghash_for_each_remove(...) \ SELECT_NAME(__VA_ARGS__, \ ghash_for_each_remove_4, \ ghash_for_each_remove_3)(__VA_ARGS__) #define ghash_for_each_steal_3(v, tbl, iter) \ for (g_hash_table_iter_init(&iter, tbl); \ g_hash_table_iter_next(&iter, NULL, (gpointer *)&v); \ g_hash_table_iter_steal(&iter)) #define ghash_for_each_steal_4(k, v, tbl, iter) \ for (g_hash_table_iter_init(&iter, tbl); \ g_hash_table_iter_next(&iter, (gpointer *)&k, (gpointer *)&v); \ g_hash_table_iter_steal(&iter)) #define ghash_for_each_steal(...) \ SELECT_NAME(__VA_ARGS__, \ ghash_for_each_steal_4, \ ghash_for_each_steal_3)(__VA_ARGS__) #endif /* __TOOLS_H__ */ ksmbd-tools-3.5.2/include/version.h000066400000000000000000000003051460410342700172370ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2020 Namjae Jeon */ #ifndef _VERSION_H #define KSMBD_TOOLS_VERSION "3.5.2" #endif /* !_VERSION_H */ ksmbd-tools-3.5.2/include/worker.h000066400000000000000000000005521460410342700170670ustar00rootroot00000000000000/* 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); void wp_init(void); #endif /* __KSMBD_WORKER_H__ */ ksmbd-tools-3.5.2/ksmbd-tools.spec000066400000000000000000000035221460410342700170740ustar00rootroot00000000000000# # 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: master Release: 0 Summary: ksmbd kernel server userspace utilities License: GPL-2.0-or-later Group: System/Filesystems Url: https://github.com/cifsd-team/ksmbd-tools Source: %{url}/archive/%{version}/%{name}-%{version}.tar.gz BuildRequires: glib2-devel BuildRequires: libnl3-devel BuildRequires: autoconf BuildRequires: automake BuildRequires: libtool BuildRequires: systemd-rpm-macros Requires(pre): kernel-default >= 5.15 Requires(pre): systemd >= 245 %description Collection of userspace utilities for the ksmbd kernel server. %prep %setup -q %build ./autogen.sh %configure --with-systemdsystemunitdir=%{_unitdir} make %{?_smp_mflags} %install %make_install %files %{_sbindir}/ksmbd.addshare %{_sbindir}/ksmbd.adduser %{_sbindir}/ksmbd.control %{_sbindir}/ksmbd.mountd %{_libexecdir}/ksmbd.tools %{_mandir}/man8/ksmbd.addshare.8* %{_mandir}/man8/ksmbd.adduser.8* %{_mandir}/man8/ksmbd.control.8* %{_mandir}/man8/ksmbd.mountd.8* %{_mandir}/man5/ksmbd.conf.5* %{_mandir}/man5/ksmbdpwd.db.5* %{_sysconfdir}/ksmbd/ksmbd.conf.example %{_unitdir}/ksmbd.service %changelog ksmbd-tools-3.5.2/ksmbd.conf.5.in000066400000000000000000000344441460410342700165100ustar00rootroot00000000000000.TH KSMBD.CONF "5" "" "ksmbd-tools @ksmbd_tools_version@" "File Formats and Conventions" .SH NAME ksmbd.conf \- the configuration file for ksmbd.mountd .SH DESCRIPTION \fBksmbd.conf\fR is the configuration file for \fBksmbd.mountd\fP(8) user mode daemon. \fBksmbd.addshare\fP(8) can be used for configuring shares for \fBksmbd.conf\fR. \fBksmbd.addshare\fR modifies \fBksmbd.conf\fR such that its existing formatting is not retained. \fBksmbd.addshare\fR notifies \fBksmbd.mountd\fR of changes, if it had made any, by sending the \fBSIGHUP\fR signal to \fBksmbd.mountd\fR. Changes made with \fBksmbd.addshare\fR will never require restarting \fBksmbd.mountd\fR and \fBksmbd\fR to take effect. \fBksmbd.control --reload\fR can be used for notifying \fBksmbd.mountd\fR of changes when not using \fBksmbd.addshare\fR. \fBksmbd.conf\fR is expected to be at \fB@sysconfdir@/ksmbd/ksmbd.conf\fR by default. \" PATH_SMBCONF A configuration file that may serve as an example can be found at \fB@sysconfdir@/ksmbd/ksmbd.conf.example\fR. .SH "FILE FORMAT" \fBksmbd.conf\fR consists of sections (i.e. groups) with each section marking the end of the previous one. A section begins with the section name enclosed in brackets (\fB[]\fR) followed by a newline. A section may contain parameters separated by newlines. A parameter consists of a name (i.e. a key) and a value, in that order, separated by an equal sign (\fB=\fR). A name may contain leading and trailing tabs and spaces. A value, which begins immediately after the equal sign, may contain leading tabs and spaces or be empty. A value may be a list of multiple values separated by commas, tabs, and spaces. For a list of users, all users in a system group are given by giving the group name prefixed with an at (\fB@\fR). A value may have a number suffix, which is either \fBK\R, \fBM\R, \fBG\R, \fBT\R, \fBP\R, or \fBE\R. A semicolon (\fB;\fR) or a hash (\fB#\fR) marks the beginning of a comment which continues until the end of the line. .SH SHARES Each section name, except that of the \fBglobal\fR section, defines a shared resource, commonly referred to as a share. A section name, which is the share name, must be UTF-8, [1, 64) bytes, and is case-insensitive. \" KSMBD_REQ_MAX_SHARE_NAME Users that may be allowed to connect to a share are those that are present in \fBksmbdpwd.db\fP(5) user database. A share may limit which users are allowed to connect to a particular share. When connected to a share, the user is mapped to a system user and underlying filesystem permissions are enforced. By default, this mapping is done by name, but it may also be done by mapping all users connected to the share to a single system user and group. When connecting as a user not in the user database, only guest sessions may work. .SH PARAMETERS Share parameters, marked below with \fB(S)\fR, can be given in any section. When a share parameter is given in a section other than \fBglobal\fR, it is specific to that particular share. Under the \fBglobal\fR section, a share parameter sets its default value for all shares. Global parameters, marked below with \fB(G)\fR, can only be given in the \fBglobal\fR section and control functionality that applies to the server. Changes to global parameters apply only after restarting \fBksmbd.mountd\fR and \fBksmbd\fR. \" .TP \" \fBadmin users\fR (S) \" \" Default: \fBadmin users = \fR .TP \fBbind interfaces only\fR (G) Only bind to interfaces given with \fBinterfaces\fR. Default: \fBbind interfaces only = no\fR .TP \fBbrowseable\fR (S) Share is seen in a net view and in the browse list. Default: \fBbrowseable = yes\fR .TP \fBcomment\fR (S) Description of the share as seen in a net view and and in the browse list. Default: \fBcomment = \fR .TP \fBcreate mask\fR (S) Octal bitmask that gets bitwise ANDed with DOS-to-UNIX-mapped permissions when creating a file. Default: \fBcreate mask = 0744\fR .TP \fBcrossmnt\fR (S) Allow path lookup to cross a mountpoint to the root of a different filesystem. Default: \fBcrossmnt = yes\fR .TP \fBdeadtime\fR (G) Number of minutes of inactivity before a connection is considered dead and is then terminated. The connection is not terminated if it has any open files. With \fBdeadtime = 0\fR, no connection is considered dead due to inactivity. Default: \fBdeadtime = 0\fR .TP \fBdirectory mask\fR (S) Octal bitmask that gets bitwise ANDed with DOS-to-UNIX-mapped permissions when creating a directory. Default: \fBdirectory mask = 0755\fR .TP \fBdurable handles\fR (G) Can grant SMB2 durable file handles on a share. Default: \fBdurable handles = no\fR \" .TP \" \fBfollow symlinks\fR (S) \" \" Default: \fBfollow symlinks = no\fR .TP \fBforce create mode\fR (S) Octal bitmask that gets bitwise ORed after the bitmask given with \fBcreate mask\fR is applied. Default: \fBforce create mode = 0000\fR .TP \fBforce directory mode\fR (S) Octal bitmask that gets bitwise ORed after the bitmask given with \fBdirectory mask\fR is applied. Default: \fBforce directory mode = 0000\fR .TP \fBforce group\fR (S) System group that all users connected to the share are mapped to. Default: \fBforce group = \fR .TP \fBforce user\fR (S) System user that all users connected to the share are mapped to. With \fBforce group = \fR, primary group of the system user is the respective system group. Default: \fBforce user = \fR .TP \fBguest account\fR (G) User that does not require a password when connecting to any share with \fBguest ok = yes\fR. When connecting to such a share with the user left empty, the parameter determines what system user to map to. Default: \fBguest account = nobody\fR .TP \fBguest account\fR (S) User that does not require a password when connecting to the share with \fBguest ok = yes\fR given. Default: \fBguest account = \fR .TP \fBguest ok\fR (S) Allow passwordless connections to the share as the user given with \fBguest account\fR and with the user left empty. Default: \fBguest ok = no\fR .TP \fBhide dot files\fR (S) Files starting with a dot appear as hidden files. Default: \fBhide dot files = yes\fR \" .TP \" \fBhosts allow\fR (S) \" \" Default: \fBhosts allow = \fR \" .TP \" \fBhosts deny\fR (S) \" \" Default: \fBhosts deny = \fR .TP \fBinherit owner\fR (S) Ownership for new files and directories is controlled by the ownership of the parent directory. Default: \fBinherit owner = no\fR .TP \fBinterfaces\fR (G) List of the interfaces that are listened to with \fBbind interfaces only = yes\fR given. Default: \fBinterfaces = \fR .TP \fBinvalid users\fR (S) List of the users that are disallowed to connect to the share. A user being in the list has precedence over it being in \fBvalid users\fR. With \fBinvalid users = \fR, no user is disallowed. Default: \fBinvalid users = \fR .TP \fBipc timeout\fR (G) Number of seconds user space has time to reply to a heartbeat frame. If exceeded, all sessions and TCP connections will be closed. With \fBipc timeout = 0\fR, user space can reply whenever. Default: \fBipc timeout = 0\fR .TP \fBkerberos keytab file (G) Path of the keytab file for the service principal. If no value is given, it is the default keytab resolved with \fBkrb5_kt_default\fP(3). Default: \fBkerberos keytab file = .TP \fBkerberos service name (G) Service principal name. If no value is given, it is \fBcifs/\fR followed by the FQDN resolved with \fBgetaddrinfo\fP(3). Default: \fBkerberos service name = .TP \fBmap to guest\fR (G) When to map a user to the user given with \fBguest account\fR. With \fBmap to guest = bad user\fR, map when the user does not exist. \" With \fBmap to guest = bad password\fR, \" With \fBmap to guest = bad uid\fR, Default: \fBmap to guest = never\fR .TP \fBmax active sessions\fR (G) Maximum number of simultaneous sessions to all shares. Default: \fBmax active sessions = 1024\fR \" KSMBD_CONF_DEFAULT_SESS_CAP .TP \fBmax connections\fR (G) Maximum number of simultaneous connections to the server. With \fBmax connections = 0\fR, the value will be set to the maximum allowed number of 65536. \" KSMBD_CONF_MAX_CONNECTIONS Number suffixes are allowed. Default: \fBmax connections = 128\fR .TP \fBmax connections\fR (S) Maximum number of simultaneous connections to the share. With \fBmax connections = 0\fR, the value will be set to the maximum allowed number of 65536. \" KSMBD_CONF_MAX_CONNECTIONS Number suffixes are allowed. Default: \fBmax connections = 128\fR .TP \fBmax open files\fR (G) Maximum number of simultaneous open files for a client. Default: \fBmax open files = 10000\fR .TP \fBnetbios name\fR (G) NetBIOS name. Default: \fBnetbios name = KSMBD SERVER\fR .TP \fBoplocks\fR (S) Issue oplocks to file open requests on the share. Default: \fBoplocks = yes\fR .TP \fBpath\fR (S) Path of the directory users connected to the share are given access to. Default: \fBpath = \fR .TP \fBread list\fR (S) List of the users that are allowed read-only access to the share. A user being in the list has precedence over \fBread only = no\fR or it being in \fBwrite list\fR. Default: \fBread list = \fR .TP \fBread only\fR (S) Users are allowed read-only access to the share. With \fBread only = no\fR, the effect is the same as with \fBwriteable = yes\fR. Default: \fBread only = ; yes\fR .TP \fBrestrict anonymous\fR (G) How to restrict connections to any share as the user given with \fBguest account\fR. With \fBrestrict anonymous = 1\fR or \fBrestrict anonymous = 2\fR, disallow connections to the \fBIPC$\fR share and any share that gives \fBguest ok = no\fR. Default: \fBrestrict anonymous = 0\fR .TP \fBroot directory\fR (G) Path of the directory prepended to \fBpath\fR of every share. Somewhat similar to \fBchroot\fP(2). Default: \fBroot directory = \fR .TP \fBserver max protocol\fR (G) Maximum protocol version supported. Default: \fBserver max protocol = SMB3_11\fR .TP \fBserver min protocol\fR (G) Minimum protocol version supported. Default: \fBserver min protocol = SMB2_10\fR .TP \fBserver multi channel support\fR (G) Use of SMB3 multi-channel is supported. SMB3 multi-channel support is experimental and may corrupt data under race conditions. Default: \fBserver multi channel support = no\fR .TP \fBserver signing\fR (G) Client is allowed or required to use SMB2 signing. With \fBserver signing = disabled\fR or \fBserver signing = auto\fR, SMB2 signing is allowed if it is requested by the client. With \fBserver signing = mandatory\fR, SMB2 signing is required. Default: \fBserver signing = disabled\fR .TP \fBserver string\fR (G) String that will appear in browse lists next to the machine name. Default: \fBserver string = SMB SERVER\fR .TP \fBshare:fake_fscaps\fR (G) Decimal bitmask that gets bitwise ORed with the filesystem capability flags so as to fake them. With \fBshare:fake_fscaps = 64\fR, the FILE_SUPPORTS_SPARSE_FILES flag is set. Default: \fBshare:fake_fscaps = 64\fR \" FILE_SUPPORTS_SPARSE_FILES .TP \fBsmb2 leases\fR (G) Negotiate SMB2 leases on file open requests. Default: \fBsmb2 leases = no\fR .TP \fBsmb2 max credits\fR (G) Maximum number of outstanding simultaneous SMB2 operations. Number suffixes are allowed. Default: \fBsmb2 max credits = 8192\fR \" SMB2_MAX_CREDITS .TP \fBsmb2 max read\fR (G) Maximum length that may be used in a SMB2 READ request sent by a client. Number suffixes are allowed. Default: \fBsmb2 max read = 4MB\fR \" SMB3_DEFAULT_IOSIZE .TP \fBsmb2 max trans\fR (G) Maximum buffer size that may be used by a client in a sent SET_INFO request or a received QUERY_INFO, QUERY_DIRECTORY, or CHANGE_NOTIFY response. Number suffixes are allowed. Default: \fBsmb2 max trans = 1MB\fR \" SMB3_DEFAULT_TRANS_SIZE .TP \fBsmb2 max write\fR (G) Maximum length that may be used in a SMB2 WRITE request sent by a client. Number suffixes are allowed. Default: \fBsmb2 max write = 4MB\fR \" SMB3_DEFAULT_IOSIZE .TP \fBsmb3 encryption\fR (G) Client is disallowed, allowed, or required to use SMB3 encryption. With \fBsmb3 encryption = disabled\fR, SMB3 encryption is disallowed even if it is requested by the client. With \fBsmb3 encryption = auto\fR, SMB3 encryption is allowed if it is requested by the client. With \fBsmb3 encryption = mandatory\fR, SMB3 encryption is required, i.e. clients that do not support encryption will be denied access to all shares. Default: \fBsmb3 encryption = auto\fR .TP \fBsmbd max io size\fR (G) Maximum read/write size of SMB-Direct. Number suffixes are allowed. Default: \fBsmbd max io size = 8MB\fR \" SMBD_DEFAULT_IOSIZE .TP \fBstore dos attributes\fR (S) Store DOS attributes using xattr and then use them in the DOS-to-UNIX-mapping of permissions. Default: \fBstore dos attributes = yes\fR .TP \fBtcp port\fR (G) TCP port that is listened to. Default: \fBtcp port = 445\fR .TP \fBvalid users\fR (S) List of the users that are allowed to connect to the share. With \fBvalid users = \fR, all users are allowed. Default: \fBvalid users = \fR .TP \fBveto files\fR (S) Names of files and directories that are made invisible and inaccessible. Names are given between forward slashes (\fB/\fR), e.g. \fBveto files = /foo/bar/\fR to make files and directories named \fBfoo\fR and \fBbar\fR invisible and inaccessible. An asterisk (\fB*\fR) and a question mark (\fB?\fR) are used for matching any number of characters and a character, respectively. Default: \fBveto files = \fR .TP \fBvfs objects\fR (S) List of the VFS modules to overload I/O operations with. Available VFS modules are \fBacl_xattr\fR and \fBstreams_xattr\fR. Default: \fBvfs objects = \fR .TP \fBworkgroup\fR (G) Workgroup the server will appear to be in when queried by clients. Default: \fBworkgroup = WORKGROUP\fR .TP \fBwritable\fR (S) Users are allowed read-write access to the share. With \fBwritable = yes\fR, the effect is the same as with \fBread only = no\fR. Default: \fBwritable = \fR .TP \fBwriteable\fR (S) Synonym for \fBwritable\fP. .TP \fBwrite list\fR (S) List of the users that are allowed read-write access to the share. A user being in the list has precedence over \fBread only = yes\fR. Default: \fBwrite list = \fR .TP \fBwrite ok\fR (S) Synonym for \fBwritable\fP. .SH COPYRIGHT Copyright \(co 2015-2022 ksmbd-tools contributors. License GPLv2: GNU GPL version 2 . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH "REPORTING BUGS" For bug reports, use the issue tracker at https://github.com/cifsd-team/ksmbd-tools/issues. .SH "SEE ALSO" .TP \fBUtilities\fR \fBksmbd.addshare\fP(8), \fBksmbd.adduser\fP(8), \fBksmbd.mountd\fP(8) ksmbd-tools-3.5.2/ksmbd.conf.example000066400000000000000000000023741460410342700173670ustar00rootroot00000000000000; see ksmbd.conf(5) for details [global] ; global parameters bind interfaces only = no deadtime = 0 guest account = nobody interfaces = ipc timeout = 0 kerberos keytab file = kerberos service name = map to guest = never max active sessions = 1024 max connections = 128 max open files = 10000 netbios name = KSMBD SERVER restrict anonymous = 0 root directory = server max protocol = SMB3_11 server min protocol = SMB2_10 server multi channel support = no server signing = disabled server string = SMB SERVER share:fake_fscaps = 64 smb2 leases = no smb2 max credits = 8192 smb2 max read = 4MB smb2 max trans = 1MB smb2 max write = 4MB smb3 encryption = auto smbd max io size = 8MB tcp port = 445 workgroup = WORKGROUP durable handles = no ; default share parameters browseable = yes comment = create mask = 0744 crossmnt = yes directory mask = 0755 force create mode = 0000 force directory mode = 0000 force group = force user = guest ok = no hide dot files = yes inherit owner = no invalid users = oplocks = yes path = read list = read only = ; yes store dos attributes = yes valid users = veto files = vfs objects = write list = [example] ; share parameters comment = read only /tmp access path = /tmp ksmbd-tools-3.5.2/ksmbd.service.in000066400000000000000000000006001460410342700170430ustar00rootroot00000000000000[Unit] Description=ksmbd userspace daemon Requires=modprobe@ksmbd.service Wants=network-online.target After=modprobe@ksmbd.service network.target network-online.target [Service] Type=forking PIDFile=@runstatedir@/ksmbd.lock ExecStart=@sbindir@/ksmbd.mountd ExecReload=@sbindir@/ksmbd.control --reload ExecStop=@sbindir@/ksmbd.control --shutdown [Install] WantedBy=multi-user.target ksmbd-tools-3.5.2/ksmbdpwd.db.5.in000066400000000000000000000031001460410342700166440ustar00rootroot00000000000000.TH KSMBDPWD.DB "5" "" "ksmbd-tools @ksmbd_tools_version@" "File Formats and Conventions" .SH NAME ksmbdpwd.db \- the user database for ksmbd.mountd .SH DESCRIPTION \fBksmbdpwd.db\fR is the user database for \fBksmbd.mountd\fP(8) user mode daemon. \fBksmbd.adduser\fP(8) may be used for configuring users for \fBksmbdpwd.db\fR. \fBksmbd.adduser\fR notifies \fBksmbd.mountd\fR of changes, if it had made any, by sending the \fBSIGHUP\fR signal to \fBksmbd.mountd\fR. \fBksmbd.control --reload\fR can be used for notifying \fBksmbd.mountd\fR of changes when not using \fBksmbd.adduser\fR. \fBksmbdpwd.db\fR is expected to be at \fB@sysconfdir@/ksmbd/ksmbdpwd.db\fR by default. \" PATH_PWDDB .SH "FILE FORMAT" \fBksmbdpwd.db\fR consists of users separated by newlines. Each user consists of a name and a password, in that order, separated by a colon (\fB:\fR). The name must be UTF-8 and [1, 48) bytes. \" KSMBD_REQ_MAX_ACCOUNT_NAME_SZ The password is created from the user-input UTF-8 password, [0, 129) bytes, by converting it to UTF-16LE, then MD4-hashing it, and finally Base64-encoding and padding it. \" MAX_NT_PWD_LEN .SH COPYRIGHT Copyright \(co 2015-2022 ksmbd-tools contributors. License GPLv2: GNU GPL version 2 . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH "REPORTING BUGS" For bug reports, use the issue tracker at https://github.com/cifsd-team/ksmbd-tools/issues. .SH "SEE ALSO" .TP \fBUtilities\fR \fBksmbd.adduser\fP(8), \fBksmbd.mountd\fP(8) ksmbd-tools-3.5.2/meson.build000066400000000000000000000065751460410342700161370ustar00rootroot00000000000000project( 'ksmbd-tools', 'c', version: run_command( '/bin/sh', '-c', ''' exec awk '/define KSMBD_TOOLS_VERSION / { gsub(/"/,"",$3); printf "%s", $3 }' include/version.h ''', check: true, ).stdout(), default_options: 'c_std=gnu99', meson_version: '>= 0.61.5', ) include_dirs = include_directories( '.', 'include', ) glib_dep = dependency( 'glib-2.0', version: '>= 2.44', ) libnl_dep = dependency( 'libnl-genl-3.0', version: '>= 3.0', ) systemd_dep = dependency( 'systemd', required: false, version: '>= 245', ) krb5_dep = dependency( get_option('krb5_name'), required: get_option('krb5'), ) asn1_lib = [] config_h_data = configuration_data() cc = meson.get_compiler('c') pthread_lib = cc.find_library( 'pthread', ) if krb5_dep.found() config_h_data.set( 'CONFIG_KRB5', krb5_dep.found(), ) config_h_data.set( 'HAVE_KRB5_AUTH_CON_GETRECVSUBKEY', cc.has_function( 'krb5_auth_con_getrecvsubkey', dependencies: krb5_dep, ), ) config_h_data.set( 'HAVE_KRB5_KEYBLOCK_KEYVALUE', cc.has_member( 'krb5_keyblock', 'keyvalue', prefix: '#include ', dependencies: krb5_dep, ), ) config_h_data.set( 'HAVE_KRB5_AUTHENTICATOR_CLIENT', cc.has_member( 'krb5_authenticator', 'client', prefix: '#include ', dependencies: krb5_dep, ), ) config_h_data.set( 'HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER', cc.compiles( ''' #include krb5_error_code krb5_auth_con_getauthenticator(krb5_context, krb5_auth_context, krb5_authenticator**); ''', dependencies: krb5_dep, name: 'krb5_auth_con_getauthenticator has krb5_authenticator** parameter', ), ) if not config_h_data.get('HAVE_KRB5_AUTHENTICATOR_CLIENT') asn1_lib = cc.find_library( 'asn1', dirs: krb5_dep.get_variable(pkgconfig: 'libdir'), ) endif endif configure_file( output: 'config.h', configuration: config_h_data, ) add_project_arguments( '-DHAVE_CONFIG_H', language: 'c', ) rundir = get_option('rundir') if rundir == '' if false # meson.version().version_compare('>= ') runstatedir = get_option('prefix') / get_option('runstatedir') else runstatedir = get_option('prefix') / get_option('localstatedir') / 'run' endif else runstatedir = rundir endif install_data( sources: 'ksmbd.conf.example', install_dir: get_option('sysconfdir') / 'ksmbd', ) systemdsystemunitdir = get_option('systemdsystemunitdir') if systemdsystemunitdir == '' systemdsystemunitdir = systemd_dep.get_variable( pkgconfig: 'systemdsystemunitdir', default_value: '', ) endif in_data = configuration_data({ 'sbindir': get_option('prefix') / get_option('sbindir'), 'sysconfdir': get_option('prefix') / get_option('sysconfdir'), 'runstatedir': runstatedir, 'ksmbd_tools_version': meson.project_version(), }) configure_file( input: 'ksmbd.conf.5.in', output: 'ksmbd.conf.5', install_dir: get_option('mandir') / 'man5', configuration: in_data, ) configure_file( input: 'ksmbdpwd.db.5.in', output: 'ksmbdpwd.db.5', install_dir: get_option('mandir') / 'man5', configuration: in_data, ) configure_file( input: 'ksmbd.service.in', output: 'ksmbd.service', install_dir: systemdsystemunitdir, configuration: in_data, ) subdir('addshare') subdir('adduser') subdir('control') subdir('mountd') subdir('tools') ksmbd-tools-3.5.2/meson_options.txt000066400000000000000000000010201460410342700174060ustar00rootroot00000000000000option( 'rundir', type: 'string', description: 'Directory to store modifiable per-process data (LOCALSTATEDIR/run by default)', ) option( 'systemdsystemunitdir', type: 'string', description: 'Directory to install systemd unit file (query pkg-config by default)', ) option( 'krb5', type: 'feature', value: 'disabled', description: 'Support for Kerberos 5 authentication', ) option( 'krb5_name', type: 'string', value: 'krb5', description: 'Dependency name used when checking Kerberos 5 support', ) ksmbd-tools-3.5.2/mountd/000077500000000000000000000000001460410342700152665ustar00rootroot00000000000000ksmbd-tools-3.5.2/mountd/Makefile.am000066400000000000000000000012501460410342700173200ustar00rootroot00000000000000AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common noinst_LIBRARIES = libmountd.a libmountd_a_SOURCES = worker.c ipc.c rpc.c rpc_srvsvc.c rpc_wkssvc.c mountd.c \ smbacl.c rpc_samr.c rpc_lsarpc.c EXTRA_DIST = ksmbd.mountd.8.in \ meson.build man_MANS = ksmbd.mountd.8 $(man_MANS): %: %.in; @$(in_script) $< >$@ CLEANFILES = $(man_MANS) install-exec-hook: uninstall-hook $(MKDIR_P) $(DESTDIR)$(sbindir) ( cd $(DESTDIR)$(sbindir) && \ $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.mountd ) uninstall-hook: -rm $(DESTDIR)$(sbindir)/ksmbd.mountd ksmbd-tools-3.5.2/mountd/ipc.c000066400000000000000000000257231460410342700162160ustar00rootroot00000000000000// 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); return NULL; } msg = g_try_malloc0(msg_sz); if (msg) msg->sz = sz; return msg; } void ipc_msg_free(struct ksmbd_ipc_msg *msg) { g_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 handle_generic_event(struct nl_cache_ops *unused, struct genl_cmd *cmd, struct genl_info *info, void *arg) { 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) { char **pp = global_conf.interfaces; int sz = 0; for (; *pp; pp++) { char *p = *pp; if (*p == 0x00) continue; sz += strlen(p) + 1; } return sz; } 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->smbd_max_io_size = global_conf.smbd_max_io_size; ev->max_connections = global_conf.max_connections; 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) { char *config_payload = KSMBD_STARTUP_CONFIG_INTERFACES(ev); char **pp = global_conf.interfaces; int sz = 0; ev->ifc_list_sz = ifc_list_sz; for (; *pp; pp++) { char *p = *pp; if (*p == 0x00) continue; strcpy(config_payload + sz, p); sz += strlen(p) + 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; } int ipc_process_event(void) { fd_set rfds; int sk_fd, ret; FD_ZERO(&rfds); sk_fd = nl_socket_get_fd(sk); FD_SET(sk_fd, &rfds); if (select(sk_fd + 1, &rfds, NULL, NULL, NULL) < 0) { if (errno != EINTR) { ret = -errno; pr_err("Can't wait on netlink socket: %m\n"); return ret; } ret = 0; return ret; } ret = nl_recvmsgs_default(sk); if (ret < 0) pr_err("nl_recv() failed, check dmesg, error: %s\n", nl_geterror(ret)); 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) { switch (genl_register_family(&ksmbd_family_ops)) { case -NLE_EXIST: case 0: genl_unregister_family(&ksmbd_family_ops); } nl_socket_free(sk); sk = NULL; } void ipc_init(void) { int ret; if (sk) return; sk = nl_socket_alloc(); if (!sk) { pr_err("Can't allocate netlink socket\n"); abort(); } nl_socket_disable_seq_check(sk); nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, nlink_msg_cb, NULL); if (nl_connect(sk, NETLINK_GENERIC)) { pr_err("Can't connect to generic netlink\n"); abort(); } ret = nl_socket_set_buffer_size(sk, KSMBD_IPC_SO_RCVBUF_SIZE, 0); if (ret) { pr_err("Can't set netlink socket buffer size: %s\n", nl_geterror(ret)); abort(); } if (genl_register_family(&ksmbd_family_ops)) { pr_err("Can't register netlink family\n"); abort(); } ret = genl_ops_resolve(sk, &ksmbd_family_ops); if (ret) { pr_err("Can't resolve netlink family\n"); abort(); } if (ipc_ksmbd_starting_up()) { pr_err("Can't send startup event\n"); abort(); } ksmbd_health_status = KSMBD_HEALTH_RUNNING; } ksmbd-tools-3.5.2/mountd/ksmbd.mountd.8.in000066400000000000000000000044441460410342700203770ustar00rootroot00000000000000.TH KSMBD.MOUNTD "8" "" "ksmbd-tools @ksmbd_tools_version@" "System Administration" .SH NAME ksmbd.mountd \- start ksmbd.mountd and ksmbd daemons .SH SYNOPSIS .B ksmbd.mountd [\-v] [\-p \fI\,PORT\/\fR] [\-n[\fI\,WAY\/\fR]] [\-C \fI\,CONF\/\fR] [\-P \fI\,PWDDB\/\fR] .SH DESCRIPTION \fBksmbd.mountd\fR starts \fBksmbd.mountd\fR user mode and \fBksmbd\fR kernel mode daemons. \fBksmbd.mountd\fR has to parse \fBksmbd.conf\fP(5) configuration file in order to start. \fBksmbd.mountd\fR can parse \fBksmbdpwd.db\fP(5) user database so as to support non-guest sessions. .SH OPTIONS .TP \fB\-p\fR, \fB\-\-port\fR=\fI\,PORT\/\fR Bind to \fI\,PORT\/\fR instead of TCP port \fB445\fR. .TP \fB\-n\fR, \fB\-\-nodetach\fR[=\fI\,WAY\/\fR] Do not detach process from foreground. If \fI\,WAY\/\fR is \fB1\fR, become process group leader (default). If \fI\,WAY\/\fR is \fB0\fR, detach. .TP \fB\-C\fR, \fB\-\-config\fR=\fI\,CONF\/\fR Use \fI\,CONF\/\fR as configuration file instead of \fB@sysconfdir@/ksmbd/ksmbd.conf\fR. \" PATH_SMBCONF .TP \fB\-P\fR, \fB\-\-pwddb\fR=\fI\,PWDDB\/\fR Use \fI\,PWDDB\/\fR as user database instead of \fB@sysconfdir@/ksmbd/ksmbdpwd.db\fR. \" PATH_PWDDB .TP \fB\-v\fR, \fB\-\-verbose\fR Be verbose. .TP \fB\-V\fR, \fB\-\-version\fR Output version information and exit. .TP \fB\-h\fR, \fB\-\-help\fR Display this help and exit. .SH "EXIT STATUS" The exit status is 0 on success and 1 on failure. When detaching process from foreground, exit status is 0 if daemonization succeeded. .SH SIGNALS \fBksmbd.mountd\fR can be notified of changes to \fBksmbd.conf\fR or \fBksmbdpwd.db\fR by sending it the \fBSIGHUP\fR signal. The manager process of \fBksmbd.mountd\fR has its PID stored in \fB@runstatedir@/ksmbd.lock\fR. \" PATH_LOCK .SH COPYRIGHT Copyright \(co 2015-2022 ksmbd-tools contributors. License GPLv2: GNU GPL version 2 . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH "REPORTING BUGS" For bug reports, use the issue tracker at https://github.com/cifsd-team/ksmbd-tools/issues. .SH "SEE ALSO" .TP \fBConfiguration File\fR \fBksmbd.conf\fP(5) .TP \fBUser Database\fR \fBksmbdpwd.db\fP(5) .TP \fBUtilities\fR \fBksmbd.addshare\fP(8), \fBksmbd.adduser\fP(8), \fBksmbd.control\fP(8) ksmbd-tools-3.5.2/mountd/meson.build000066400000000000000000000013151460410342700174300ustar00rootroot00000000000000mountd_lib = static_library( 'mountd', 'worker.c', 'ipc.c', 'rpc.c', 'rpc_srvsvc.c', 'rpc_wkssvc.c', 'mountd.c', 'smbacl.c', 'rpc_samr.c', 'rpc_lsarpc.c', include_directories: include_dirs, c_args: [ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')), '-DRUNSTATEDIR="@0@"'.format(runstatedir), ], dependencies: [ glib_dep, libnl_dep, ], ) configure_file( input: 'ksmbd.mountd.8.in', output: 'ksmbd.mountd.8', install_dir: get_option('mandir') / 'man8', configuration: in_data, ) install_symlink( 'ksmbd.mountd', install_dir: get_option('sbindir'), pointing_to: get_option('prefix') / get_option('libexecdir') / 'ksmbd.tools', ) ksmbd-tools-3.5.2/mountd/mountd.c000066400000000000000000000230131460410342700167370ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. * * linux-cifsd-devel@lists.sourceforge.net */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "ipc.h" #include "management/share.h" #include "config_parser.h" static void usage(int status) { printf( "Usage: ksmbd.mountd [-v] [-p PORT] [-n[WAY]] [-C CONF] [-P PWDDB]\n"); if (status != EXIT_SUCCESS) printf("Try `ksmbd.mountd --help' for more information.\n"); else printf( "\n" " -p, --port=PORT bind to PORT instead of TCP port 445\n" " -n, --nodetach[=WAY] do not detach process from foreground;\n" " if WAY is 1, become process group leader (default);\n" " if WAY is 0, detach\n" " -C, --config=CONF use CONF as configuration file instead of\n" " `" PATH_SMBCONF "'\n" " -P, --pwddb=PWDDB use PWDDB as user database instead of\n" " `" PATH_PWDDB "'\n" " -v, --verbose be verbose\n" " -V, --version output version information and exit\n" " -h, --help display this help and exit\n" "\n" "See ksmbd.mountd(8) for more details.\n"); } static struct option opts[] = { {"port", required_argument, NULL, 'p' }, {"nodetach", optional_argument, NULL, 'n' }, {"config", required_argument, NULL, 'C' }, {"pwddb", required_argument, NULL, 'P' }, {"verbose", no_argument, NULL, 'v' }, {"version", no_argument, NULL, 'V' }, {"help", no_argument, NULL, 'h' }, {NULL, 0, NULL, 0 } }; #define LIST_FMT_SS "%28s %s" static void __list_config_share_cb(struct ksmbd_share *share, int *wfd) { dprintf(*wfd, LIST_FMT_SS "\n", share->name, share->comment ?: ""); } static int list_config(int wfd) { dprintf(wfd, "\e[1m" LIST_FMT_SS "\e[m" "\n", "Name", "Comment"); shm_iter_shares((share_cb)__list_config_share_cb, &wfd); return kill(global_conf.pid, SIGUSR1) < 0 ? -errno : 0; } static void worker_sa_sigaction(int signo, siginfo_t *siginfo, void *ucontext) { switch (signo) { case SIGIO: case SIGPIPE: case SIGCHLD: return; case SIGHUP: ksmbd_health_status |= KSMBD_SHOULD_RELOAD_CONFIG; return; case SIGUSR1: ksmbd_health_status |= KSMBD_SHOULD_LIST_CONFIG; return; case SIGINT: case SIGQUIT: case SIGTERM: ksmbd_health_status &= ~KSMBD_HEALTH_RUNNING; return; } _Exit(128 + signo); } static void worker_init_sa_handler(sigset_t sigset) { int signo; for (signo = 1; signo < _NSIG; signo++) if (sigismember(&sigset, signo)) { struct sigaction act = { .sa_sigaction = worker_sa_sigaction, .sa_flags = SA_SIGINFO, }; sigfillset(&act.sa_mask); sigaction(signo, &act, NULL); } } static void __splice_pipe(int rfd, int wfd) { if (wfd >= 0) { while (splice(rfd, NULL, wfd, NULL, PIPE_BUF, 0) > 0) ; } else { char buf[PIPE_BUF]; while (read(rfd, buf, sizeof(buf)) > 0) ; } } static int worker_init_wait(pid_t pid, sigset_t sigset, int rfd) { int ret = -ECHILD, wfd = -1; if (fcntl(rfd, F_SETFL, fcntl(rfd, F_GETFL) | O_ASYNC) < 0 || fcntl(rfd, F_SETOWN, global_conf.pid) < 0) { ret = -errno; pr_err("Can't control pipe: %m\n"); return ret; } pr_info("Started worker\n"); for (;;) { siginfo_t siginfo; if (sigwaitinfo(&sigset, &siginfo) < 0) continue; if (siginfo.si_signo == SIGIO) { __splice_pipe(rfd, wfd); continue; } if (siginfo.si_signo == SIGPIPE) { if (wfd >= 0) { close(wfd); wfd = -1; } continue; } if (siginfo.si_signo == SIGCHLD) { if (siginfo.si_code == CLD_KILLED) siginfo.si_status += 128; else if (siginfo.si_code != CLD_EXITED && siginfo.si_code != CLD_DUMPED) continue; if (siginfo.si_status > 128) { int signo = siginfo.si_status - 128; if (!sigismember(&sigset, signo)) ret = -EIO; pr_err("Worker " "%s" "killed: %s\n", ret == -EIO ? "fatally " : "", strsignal(signo)); } else if (siginfo.si_status != EXIT_SUCCESS) { ret = -EIO; } __splice_pipe(rfd, wfd); if (wfd >= 0) close(wfd); return ret; } if (siginfo.si_signo == SIGINT || siginfo.si_signo == SIGQUIT || siginfo.si_signo == SIGTERM) { ret = 0; } else if (siginfo.si_signo == SIGUSR1 && siginfo.si_pid == pid) { __splice_pipe(rfd, wfd); if (wfd >= 0) { close(wfd); wfd = -1; } continue; } if (kill(pid, siginfo.si_signo) < 0) continue; if (siginfo.si_signo == SIGUSR1) { g_autofree char *fifo_path = g_strdup_printf("%s.%d", PATH_FIFO, siginfo.si_pid); if (wfd >= 0) continue; wfd = open(fifo_path, O_WRONLY | O_NONBLOCK); if (wfd < 0) pr_err("Can't open `%s': %m\n", fifo_path); } } } static int worker_init(void) { sigset_t sigset; pid_t pid; int fds[2], ret; sigemptyset(&sigset); sigaddset(&sigset, SIGIO); sigaddset(&sigset, SIGPIPE); sigaddset(&sigset, SIGCHLD); sigaddset(&sigset, SIGHUP); sigaddset(&sigset, SIGUSR1); sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGQUIT); sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGABRT); pthread_sigmask(SIG_BLOCK, &sigset, NULL); if (pipe2(fds, O_NONBLOCK) < 0) { ret = -errno; pr_err("Can't create pipe: %m\n"); return ret; } pid = fork(); if (pid < 0) { ret = -errno; pr_err("Can't fork worker: %m\n"); close(fds[1]); close(fds[0]); return ret; } if (pid > 0) { close(fds[1]); ret = worker_init_wait(pid, sigset, fds[0]); close(fds[0]); return ret; } close(fds[0]); worker_init_sa_handler(sigset); ret = load_config(global_conf.pwddb, global_conf.smbconf); if (ret) goto out; for (;;) { pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); ret = ipc_process_event(); pthread_sigmask(SIG_BLOCK, &sigset, NULL); if (ret || !(ksmbd_health_status & KSMBD_HEALTH_RUNNING)) goto out; if (ksmbd_health_status & KSMBD_SHOULD_RELOAD_CONFIG) { ret = load_config(global_conf.pwddb, global_conf.smbconf); if (!ret) { pr_info("Reloaded config\n"); ksmbd_health_status &= ~KSMBD_SHOULD_RELOAD_CONFIG; } } if (ksmbd_health_status & KSMBD_SHOULD_LIST_CONFIG) { ret = list_config(fds[1]); if (!ret) { pr_info("Listed config\n"); ksmbd_health_status &= ~KSMBD_SHOULD_LIST_CONFIG; } } } out: close(fds[1]); remove_config(); return ret; } static int manager_init_wait(sigset_t sigset) { pr_info("Started manager\n"); for (;;) { siginfo_t siginfo; if (sigwaitinfo(&sigset, &siginfo) < 0) continue; if (siginfo.si_signo == SIGCHLD) { if (siginfo.si_code != CLD_KILLED && siginfo.si_code != CLD_EXITED && siginfo.si_code != CLD_DUMPED) continue; pr_err("Can't init manager, check syslog\n"); return -ECHILD; } if (siginfo.si_signo == SIGUSR1) { if (siginfo.si_pid != global_conf.pid) continue; return 0; } } } static int manager_init(int nodetach) { int signo; sigset_t sigset; int ret; for (signo = 1; signo < _NSIG; signo++) { struct sigaction act = { .sa_handler = SIG_DFL, .sa_flags = signo == SIGCHLD ? SA_NOCLDWAIT : 0, }; sigfillset(&act.sa_mask); sigaction(signo, &act, NULL); } sigemptyset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); switch (nodetach) { case 0: sigaddset(&sigset, SIGCHLD); sigaddset(&sigset, SIGUSR1); pthread_sigmask(SIG_BLOCK, &sigset, NULL); global_conf.pid = fork(); if (global_conf.pid < 0) { ret = -errno; pr_err("Can't fork manager: %m\n"); return ret; } if (global_conf.pid > 0) return manager_init_wait(sigset); setsid(); if (!freopen("/dev/null", "r", stdin) || !freopen("/dev/null", "w", stdout) || !freopen("/dev/null", "w", stderr)) { ret = -errno; pr_err("Can't redirect stream: %m\n"); return ret; } pr_logger_init(PR_LOGGER_SYSLOG); pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); break; case 1: setpgid(0, 0); } ret = cp_parse_lock(); if (ret) return ret; if (cp_parse_subauth()) pr_info("Ignored subauth file\n"); if (!nodetach) { pid_t ppid = getppid(); if (ppid == 1) return -ESRCH; if (kill(ppid, SIGUSR1) < 0) { ret = -errno; pr_err("Can't send SIGUSR1 to PID %d: %m\n", ppid); return ret; } } for (;;) { ret = worker_init(); switch (ret) { case -ECHILD: sleep(1); continue; default: pr_info("Terminated\n"); return ret; } } } int mountd_main(int argc, char **argv) { int ret = -EINVAL; int nodetach = 0; int c; while ((c = getopt_long(argc, argv, "p:n::C:P:vVh", opts, NULL)) != EOF) switch (c) { case 'p': global_conf.tcp_port = cp_get_group_kv_long(optarg); break; case 'n': nodetach = !optarg ?: cp_get_group_kv_long(optarg); break; case 'C': g_free(global_conf.smbconf); global_conf.smbconf = g_strdup(optarg); break; case 'P': g_free(global_conf.pwddb); global_conf.pwddb = g_strdup(optarg); break; case 'v': set_log_level(PR_DEBUG); break; case 'V': ret = show_version(); goto out; case 'h': ret = 0; /* Fall through */ case '?': default: usage(ret ? EXIT_FAILURE : EXIT_SUCCESS); goto out; } if (argc > optind) { usage(ret ? EXIT_FAILURE : EXIT_SUCCESS); goto out; } if (!global_conf.smbconf) global_conf.smbconf = g_strdup(PATH_SMBCONF); if (!global_conf.pwddb) global_conf.pwddb = g_strdup(PATH_PWDDB); ret = manager_init(nodetach); out: return ret ? EXIT_FAILURE : EXIT_SUCCESS; } ksmbd-tools-3.5.2/mountd/rpc.c000066400000000000000000000766211460410342700162320ustar00rootroot00000000000000// 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)) g_free(dce->payload); g_free(dce); } static struct ksmbd_dcerpc *dcerpc_ext_alloc(unsigned int flags, void *payload, int payload_sz) { struct ksmbd_dcerpc *dce; dce = g_try_malloc0(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); if (pipe->entries) g_ptr_array_free(pipe->entries, 1); g_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 = g_try_malloc0(sizeof(struct ksmbd_rpc_pipe)); if (!pipe) return NULL; pipe->id = -1; pipe->entries = g_ptr_array_new(); 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 __clear_pipes_table(void) { struct ksmbd_rpc_pipe *pipe; GHashTableIter iter; g_rw_lock_writer_lock(&pipes_table_lock); ghash_for_each(pipe, pipes_table, iter) __rpc_pipe_free(pipe); 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 = g_try_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) \ { \ align_offset(dce, sizeof(type)); \ if (try_realloc_payload(dce, sizeof(value))) \ return -ENOMEM; \ 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) \ int ndr_read_##name(struct ksmbd_dcerpc *dce, type *value) \ { \ type ret; \ \ align_offset(dce, sizeof(type)); \ if (dce->offset + sizeof(type) > dce->payload_sz) \ return -EINVAL; \ \ if (dce->flags & KSMBD_DCERPC_LITTLE_ENDIAN) \ ret = le(*(type *)PAYLOAD_HEAD(dce)); \ else \ ret = be(*(type *)PAYLOAD_HEAD(dce)); \ dce->offset += sizeof(type); \ if (value) \ *value = ret; \ return 0; \ } 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); \ if (ret) \ return ret; \ ret = ndr_write_##name(dce, value); \ return ret; \ } NDR_WRITE_UNION(int16, __u16); NDR_WRITE_UNION(int32, __u32); #define NDR_READ_UNION(name, type) \ int ndr_read_union_##name(struct ksmbd_dcerpc *dce, type *value) \ { \ type val1, val2; \ \ if (ndr_read_##name(dce, &val1)) \ return -EINVAL; \ if (ndr_read_##name(dce, &val2)) \ return -EINVAL; \ if (val1 != val2) { \ pr_err("NDR: union representation mismatch %lu\n", \ (unsigned long)val1); \ return -EINVAL; \ } \ if (value) \ *value = val1; \ return 0; \ } NDR_READ_UNION(int32, __u32); int ndr_write_bytes(struct ksmbd_dcerpc *dce, void *value, size_t sz) { align_offset(dce, 2); if (try_realloc_payload(dce, sz)) return -ENOMEM; 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); if (dce->offset + sz > dce->payload_sz) return -EINVAL; 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) { g_autofree char *out = NULL; 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); if (ret) return ret; ret = ndr_write_int32(dce, 0); if (ret) return ret; ret = ndr_write_int32(dce, str_len); if (ret) return ret; ret = ndr_write_bytes(dce, out, bytes_written); if (ret) return ret; auto_align_offset(dce); return ret; } int ndr_write_string(struct ksmbd_dcerpc *dce, char *str) { g_autofree char *out = NULL; 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 if (ret) return ret; ret = ndr_write_int32(dce, 0); if (ret) return ret; ret = ndr_write_int32(dce, len); // actual count if (ret) return ret; ret = ndr_write_bytes(dce, out, bytes_written); auto_align_offset(dce); return ret; } int ndr_write_lsa_string(struct ksmbd_dcerpc *dce, char *str) { g_autofree char *out = NULL; 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 if (ret) return ret; ret = ndr_write_int32(dce, 0); if (ret) return ret; ret = ndr_write_int32(dce, len); // actual count if (ret) return ret; ret = ndr_write_bytes(dce, out, bytes_written); auto_align_offset(dce); return ret; } char *ndr_read_vstring(struct ksmbd_dcerpc *dce) { gchar *out; gsize bytes_read = 0; gsize bytes_written = 0; int raw_len; int charset = KSMBD_CHARSET_UTF16LE; if (ndr_read_int32(dce, &raw_len)) return NULL; /* read in offset */ if (ndr_read_int32(dce, NULL)) return NULL; if (ndr_read_int32(dce, NULL)) return NULL; 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 = g_strdup(""); return out; } if (dce->offset + 2 * raw_len > dce->payload_sz) return NULL; 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; } int ndr_read_vstring_ptr(struct ksmbd_dcerpc *dce, struct ndr_char_ptr *ctr) { ctr->ptr = ndr_read_vstring(dce); if (!ctr->ptr) return -EINVAL; return 0; } int ndr_read_uniq_vstring_ptr(struct ksmbd_dcerpc *dce, struct ndr_uniq_char_ptr *ctr) { if (ndr_read_int32(dce, &ctr->ref_id)) return -EINVAL; if (ctr->ref_id == 0) { ctr->ptr = NULL; return 0; } ctr->ptr = ndr_read_vstring(dce); if (!ctr->ptr) return -EINVAL; return 0; } void ndr_free_vstring_ptr(struct ndr_char_ptr *ctr) { g_free(ctr->ptr); ctr->ptr = NULL; } void ndr_free_uniq_vstring_ptr(struct ndr_uniq_char_ptr *ctr) { ctr->ref_id = 0; g_free(ctr->ptr); ctr->ptr = NULL; } int ndr_read_ptr(struct ksmbd_dcerpc *dce, struct ndr_ptr *ctr) { if (ndr_read_int32(dce, &ctr->ptr)) return -EINVAL; return 0; } int ndr_read_uniq_ptr(struct ksmbd_dcerpc *dce, struct ndr_uniq_ptr *ctr) { if (ndr_read_int32(dce, &ctr->ref_id)) return -EINVAL; if (ctr->ref_id == 0) { ctr->ptr = 0; return 0; } if (ndr_read_int32(dce, &ctr->ptr)) return -EINVAL; return 0; } 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++) { void *entry; entry = g_ptr_array_index(pipe->entries, 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++) { void *entry; entry = g_ptr_array_index(pipe->entries, i); if (dce->entry_rep(dce, entry)) return KSMBD_RPC_EBAD_DATA; } for (i = 0; i < max_entry_nr; i++) { void *entry; entry = g_ptr_array_index(pipe->entries, 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++; if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) return KSMBD_RPC_EBAD_DATA; 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; /* * 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 (ndr_write_int32(dce, max_entry_nr)) return KSMBD_RPC_EBAD_DATA; /* * ARRAY representation [per dimension] * max_count * offset * actual_count * element representation [1..N] * actual elements [1..N] */ if (ndr_write_int32(dce, max_entry_nr)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 1)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, max_entry_nr)) return KSMBD_RPC_EBAD_DATA; 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); } void rpc_init(void) { if (!pipes_table) pipes_table = g_hash_table_new(g_int_hash, g_int_equal); rpc_samr_init(); rpc_lsarpc_init(); } void rpc_destroy(void) { rpc_lsarpc_destroy(); rpc_samr_destroy(); if (pipes_table) { __clear_pipes_table(); g_hash_table_destroy(pipes_table); pipes_table = NULL; } } static int dcerpc_hdr_write(struct ksmbd_dcerpc *dce, struct dcerpc_header *hdr) { int ret; ret = ndr_write_int8(dce, hdr->rpc_vers); if (ret) return ret; ret = ndr_write_int8(dce, hdr->rpc_vers_minor); if (ret) return ret; ret = ndr_write_int8(dce, hdr->ptype); if (ret) return ret; ret = ndr_write_int8(dce, hdr->pfc_flags); if (ret) return ret; ret = ndr_write_bytes(dce, &hdr->packed_drep, sizeof(hdr->packed_drep)); if (ret) return ret; ret = ndr_write_int16(dce, hdr->frag_length); if (ret) return ret; ret = ndr_write_int16(dce, hdr->auth_length); if (ret) return ret; ret = ndr_write_int32(dce, hdr->call_id); return ret; } static int dcerpc_hdr_read(struct ksmbd_dcerpc *dce, struct dcerpc_header *hdr) { /* Common Type Header for the Serialization Stream */ if (ndr_read_int8(dce, &hdr->rpc_vers)) return -EINVAL; if (ndr_read_int8(dce, &hdr->rpc_vers_minor)) return -EINVAL; if (ndr_read_int8(dce, &hdr->ptype)) return -EINVAL; if (ndr_read_int8(dce, &hdr->pfc_flags)) return -EINVAL; /* * 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. */ if (ndr_read_bytes(dce, &hdr->packed_drep, sizeof(hdr->packed_drep))) return -EINVAL; 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; if (ndr_read_int16(dce, &hdr->frag_length)) return -EINVAL; if (ndr_read_int16(dce, &hdr->auth_length)) return -EINVAL; if (ndr_read_int32(dce, &hdr->call_id)) return -EINVAL; return 0; } static int dcerpc_response_hdr_write(struct ksmbd_dcerpc *dce, struct dcerpc_response_header *hdr) { int ret; ret = ndr_write_int32(dce, hdr->alloc_hint); if (ret) return ret; ret = ndr_write_int16(dce, hdr->context_id); if (ret) return ret; ret = ndr_write_int8(dce, hdr->cancel_count); auto_align_offset(dce); return ret; } static int dcerpc_request_hdr_read(struct ksmbd_dcerpc *dce, struct dcerpc_request_header *hdr) { if (ndr_read_int32(dce, &hdr->alloc_hint)) return -EINVAL; if (ndr_read_int16(dce, &hdr->context_id)) return -EINVAL; if (ndr_read_int16(dce, &hdr->opnum)) return -EINVAL; return 0; } int dcerpc_write_headers(struct ksmbd_dcerpc *dce, int method_status) { int payload_offset, ret; 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; ret = dcerpc_hdr_write(dce, &dce->hdr); if (ret) return ret; /* 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; ret = dcerpc_response_hdr_write(dce, &dce->resp_hdr); if (ret) return ret; dce->offset = payload_offset; return 0; } static int __dcerpc_read_syntax(struct ksmbd_dcerpc *dce, struct dcerpc_syntax *syn) { if (ndr_read_int32(dce, &syn->uuid.time_low)) return -EINVAL; if (ndr_read_int16(dce, &syn->uuid.time_mid)) return -EINVAL; if (ndr_read_int16(dce, &syn->uuid.time_hi_and_version)) return -EINVAL; if (ndr_read_bytes(dce, syn->uuid.clock_seq, sizeof(syn->uuid.clock_seq))) return -EINVAL; if (ndr_read_bytes(dce, syn->uuid.node, sizeof(syn->uuid.node))) return -EINVAL; if (ndr_read_int16(dce, &syn->ver_major)) return -EINVAL; if (ndr_read_int16(dce, &syn->ver_minor)) return -EINVAL; return 0; } static int __dcerpc_write_syntax(struct ksmbd_dcerpc *dce, struct dcerpc_syntax *syn) { int ret; ret = ndr_write_int32(dce, syn->uuid.time_low); if (ret) return ret; ret = ndr_write_int16(dce, syn->uuid.time_mid); if (ret) return ret; ret = ndr_write_int16(dce, syn->uuid.time_hi_and_version); if (ret) return ret; ret = ndr_write_bytes(dce, syn->uuid.clock_seq, sizeof(syn->uuid.clock_seq)); if (ret) return ret; ret = ndr_write_bytes(dce, syn->uuid.node, sizeof(syn->uuid.node)); if (ret) return ret; ret = ndr_write_int16(dce, syn->ver_major); if (ret) return ret; ret = ndr_write_int16(dce, syn->ver_minor); return ret; } static void dcerpc_bind_req_free(struct dcerpc_bind_request *hdr) { int i; for (i = 0; i < hdr->num_contexts; i++) g_free(hdr->list[i].transfer_syntaxes); g_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; int ret = -EINVAL; hdr->flags = dce->rpc_req->flags; if (ndr_read_int16(dce, &hdr->max_xmit_frag_sz)) return -EINVAL; if (ndr_read_int16(dce, &hdr->max_recv_frag_sz)) return -EINVAL; if (ndr_read_int32(dce, &hdr->assoc_group_id)) return -EINVAL; if (ndr_read_int8(dce, &hdr->num_contexts)) return -EINVAL; hdr->list = NULL; auto_align_offset(dce); if (!hdr->num_contexts) return 0; hdr->list = g_try_malloc0_n(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]; if (ndr_read_int16(dce, &ctx->id)) goto fail; if (ndr_read_int8(dce, &ctx->num_syntaxes)) goto fail; if (!ctx->num_syntaxes) { pr_err("BIND: zero syntaxes provided\n"); goto fail; } __dcerpc_read_syntax(dce, &ctx->abstract_syntax); ctx->transfer_syntaxes = g_try_malloc0_n(ctx->num_syntaxes, sizeof(struct dcerpc_syntax)); if (!ctx->transfer_syntaxes) { ret = -ENOMEM; goto fail; } for (j = 0; j < ctx->num_syntaxes; j++) __dcerpc_read_syntax(dce, &ctx->transfer_syntaxes[j]); } return KSMBD_RPC_OK; fail: g_free(hdr->list); return ret; } 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); if (ndr_write_int16(dce, DCERPC_BIND_NAK_RSN_PROTOCOL_VERSION_NOT_SUPPORTED)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int8(dce, ARRAY_SIZE(known_syntaxes))) return KSMBD_RPC_EBAD_DATA; auto_align_offset(dce); for (i = 0; i < ARRAY_SIZE(known_syntaxes); i++) { if (ndr_write_int8(dce, known_syntaxes[i].syn.ver_major)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int8(dce, known_syntaxes[i].syn.ver_minor)) return KSMBD_RPC_EBAD_DATA; } 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; if (dcerpc_hdr_write(dce, &dce->hdr)) return KSMBD_RPC_EBAD_DATA; 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; if (ndr_write_int16(dce, dce->bi_req.max_xmit_frag_sz)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, dce->bi_req.max_recv_frag_sz)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, dce->bi_req.assoc_group_id)) return KSMBD_RPC_EBAD_DATA; 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) { if (ndr_write_int16(dce, 0)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, 0)) return KSMBD_RPC_EBAD_DATA; } else { if (ndr_write_int16(dce, strlen(addr))) return KSMBD_RPC_EBAD_DATA; if (ndr_write_bytes(dce, addr, strlen(addr))) return KSMBD_RPC_EBAD_DATA; } align_offset(dce, 4); /* [flag(NDR_ALIGN4)] DATA_BLOB _pad1; */ num_trans = dce->bi_req.num_contexts; if (ndr_write_int8(dce, num_trans)) return KSMBD_RPC_EBAD_DATA; 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; if (ndr_write_union_int16(dce, result)) return KSMBD_RPC_EBAD_DATA; } else { if (result == DCERPC_BIND_ACK_RES_ACCEPT) { if (ndr_write_union_int16(dce, result)) return KSMBD_RPC_EBAD_DATA; } if (result == DCERPC_BIND_ACK_RES_NEGOTIATE_ACK) { if (ndr_write_int16(dce, result)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, 0x00)) return KSMBD_RPC_EBAD_DATA; s = &negotiate_ack_PNIO_uuid; } } if (__dcerpc_write_syntax(dce, s)) return KSMBD_RPC_EBAD_DATA; } 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; if (dcerpc_hdr_write(dce, &dce->hdr)) return KSMBD_RPC_EBAD_DATA; 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.5.2/mountd/rpc_lsarpc.c000066400000000000000000000414551460410342700175730ustar00rootroot00000000000000// 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); g_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 = g_try_malloc0(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 if (ret) return ret; ret = ndr_write_int16(dce, (len+1)*2); // size if (ret) return ret; dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); /* ref pointer for domain name*/ if (ret) return ret; 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 if (ret) return ret; ret = ndr_write_int32(dce, sid->num_subauth); // count if (ret) return ret; ret = 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; __u16 val; if (ndr_read_int16(dce, &val)) return KSMBD_RPC_EINVALID_PARAMETER; dce->lr_req.level = val; 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++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, 1)) // count return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, 0)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, DS_ROLE_STANDALONE_SERVER)) // role return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, 0)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) // flags return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) // NULL pointer : Pointer to Dns Domain return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) // NULL pointer : Pointer to Forest return KSMBD_RPC_EBAD_DATA; /* NULL Domain guid */ for (i = 0; i < 16; i++) { if (ndr_write_int8(dce, 0)) return KSMBD_RPC_EBAD_DATA; } if (ndr_write_vstring(dce, domain_name)) // domain string return KSMBD_RPC_EBAD_DATA; 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 */ if (ndr_write_bytes(dce, ph->handle, HANDLE_SIZE)) return KSMBD_RPC_EBAD_DATA; return KSMBD_RPC_OK; } static int lsarpc_query_info_policy_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; __u16 val; if (ndr_read_bytes(dce, dce->lr_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; // level if (ndr_read_int16(dce, &val)) return KSMBD_RPC_EINVALID_PARAMETER; dce->lr_req.level = val; 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++; // ref pointer if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; // level if (ndr_write_int16(dce, LSA_POLICY_INFO_ACCOUNT_DOMAIN)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, 0)) return KSMBD_RPC_EBAD_DATA; /* Domain, Sid ref pointer */ if (lsa_domain_account_rep(dce, domain_name)) return KSMBD_RPC_EBAD_DATA; /* Pointer to domain, Sid */ smb_init_domain_sid(&sid); if (lsa_domain_account_data(dce, domain_name, &sid)) return KSMBD_RPC_EBAD_DATA; return KSMBD_RPC_OK; } static int __lsarpc_entry_processed(struct ksmbd_rpc_pipe *pipe, int i) { struct lsarpc_names_info *ni; ni = g_ptr_array_remove_index(pipe->entries, i); g_free(ni); return 0; } static int lsarpc_lookup_sid2_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct lsarpc_names_info *ni = NULL; unsigned int num_sid, i; if (ndr_read_bytes(dce, dce->lr_req.handle, HANDLE_SIZE)) goto fail; if (ndr_read_int32(dce, &num_sid)) goto fail; // ref pointer if (ndr_read_int32(dce, NULL)) goto fail; // max count if (ndr_read_int32(dce, NULL)) goto fail; for (i = 0; i < num_sid; i++) if (ndr_read_int32(dce, NULL)) // ref pointer goto fail; for (i = 0; i < num_sid; i++) { struct passwd *passwd; int rid; ni = g_try_malloc0(sizeof(struct lsarpc_names_info)); if (!ni) break; // max count if (ndr_read_int32(dce, NULL)) goto fail; // sid if (smb_read_sid(dce, &ni->sid)) goto fail; 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, sizeof(ni->domain_str), &ni->type)) ni->index = -1; g_ptr_array_add(pipe->entries, ni); pipe->num_entries++; } pipe->entry_processed = __lsarpc_entry_processed; return KSMBD_RPC_OK; fail: g_free(ni); return KSMBD_RPC_EINVALID_PARAMETER; } 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++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, pipe->num_entries)) // count return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 32)) // max size return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, pipe->num_entries)) // max count return KSMBD_RPC_EBAD_DATA; for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; ni = g_ptr_array_index(pipe->entries, i); if (ni->type == -1) rc = KSMBD_RPC_SOME_NOT_MAPPED; if (lsa_domain_account_rep(dce, ni->domain_str)) return KSMBD_RPC_EBAD_DATA; } for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; ni = g_ptr_array_index(pipe->entries, i); if (lsa_domain_account_data(dce, ni->domain_str, &ni->sid)) return KSMBD_RPC_EBAD_DATA; } /* Pointer to Names */ if (ndr_write_int32(dce, pipe->num_entries)) // count return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; // ref pointer if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, pipe->num_entries)) // max count return KSMBD_RPC_EBAD_DATA; for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; size_t len; ni = g_ptr_array_index(pipe->entries, i); if (ndr_write_int16(dce, ni->type)) // sid type return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, 0)) return KSMBD_RPC_EBAD_DATA; if (ni->user) len = strlen(ni->user->name); else len = strlen("None"); if (ndr_write_int16(dce, len*2)) // length return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, len*2)) // size return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; // ref pointer if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, ni->index)) // sid index return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) // unknown return KSMBD_RPC_EBAD_DATA; } for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; char *username = "None"; ni = g_ptr_array_index(pipe->entries, i); if (ni->user) username = ni->user->name; if (ndr_write_string(dce, username)) // username return KSMBD_RPC_EBAD_DATA; } if (ndr_write_int32(dce, pipe->num_entries)) // count return KSMBD_RPC_EBAD_DATA; 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 lsarpc_names_info *ni = NULL; struct ndr_uniq_char_ptr username; int num_names, i; if (ndr_read_bytes(dce, dce->lr_req.handle, HANDLE_SIZE)) goto fail; // num names if (ndr_read_int32(dce, &num_names)) goto fail; // max count if (ndr_read_int32(dce, NULL)) goto fail; for (i = 0; i < num_names; i++) { char *name = NULL; ni = g_try_malloc(sizeof(struct lsarpc_names_info)); if (!ni) break; // length if (ndr_read_int16(dce, NULL)) goto fail; // size if (ndr_read_int16(dce, NULL)) goto fail; if (ndr_read_uniq_vstring_ptr(dce, &username)) goto fail; if (STR_VAL(username) && strstr(STR_VAL(username), "\\")) { strtok(STR_VAL(username), "\\"); name = strtok(NULL, "\\"); } ni->user = usm_lookup_user(name); ndr_free_uniq_vstring_ptr(&username); if (!ni->user) { g_free(ni); break; } g_ptr_array_add(pipe->entries, ni); pipe->num_entries++; smb_init_domain_sid(&ni->sid); } pipe->entry_processed = __lsarpc_entry_processed; return KSMBD_RPC_OK; fail: g_free(ni); return KSMBD_RPC_EINVALID_PARAMETER; } 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++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 1)) // domain count return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 32)) // max size return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 1)) // max count return KSMBD_RPC_EBAD_DATA; if (lsa_domain_account_rep(dce, domain_name)) return KSMBD_RPC_EBAD_DATA; smb_init_domain_sid(&sid); if (lsa_domain_account_data(dce, domain_name, &sid)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, pipe->num_entries)) // count return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // sid ref id return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, pipe->num_entries)) // count return KSMBD_RPC_EBAD_DATA; for (i = 0; i < pipe->num_entries; i++) { if (ndr_write_int16(dce, SID_TYPE_USER)) // sid type return KSMBD_RPC_EBAD_DATA; if (ndr_write_int16(dce, 0)) return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, i)) // sid index return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) return KSMBD_RPC_EBAD_DATA; } for (i = 0; i < pipe->num_entries; i++) { struct lsarpc_names_info *ni; ni = g_ptr_array_index(pipe->entries, i); ni->sid.sub_auth[ni->sid.num_subauth++] = ni->user->uid; if (ndr_write_int32(dce, ni->sid.num_subauth)) // sid auth count return KSMBD_RPC_EBAD_DATA; if (smb_write_sid(dce, &ni->sid)) // sid return KSMBD_RPC_EBAD_DATA; } if (ndr_write_int32(dce, pipe->num_entries)) return KSMBD_RPC_EBAD_DATA; 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; if (ndr_read_bytes(dce, dce->lr_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; 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); if (ndr_write_int64(dce, 0)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int64(dce, 0)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) return KSMBD_RPC_EBAD_DATA; 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 */ if (ndr_write_int32(dce, status)) return KSMBD_RPC_EBAD_DATA; if (dcerpc_write_headers(dce, status)) return KSMBD_RPC_EBAD_DATA; 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); } static void lsarpc_ph_clear_table(void) { struct policy_handle *ph; GHashTableIter iter; g_rw_lock_writer_lock(&ph_table_lock); ghash_for_each(ph, ph_table, iter) g_free(ph); g_rw_lock_writer_unlock(&ph_table_lock); } void rpc_lsarpc_init(void) { if (!domain_name) { char hostname[NAME_MAX]; /* * ksmbd supports the standalone server and * uses the hostname as the domain name. */ if (gethostname(hostname, NAME_MAX)) abort(); domain_name = g_ascii_strup(hostname, -1); } if (!ph_table) ph_table = g_hash_table_new(g_str_hash, g_str_equal); } void rpc_lsarpc_destroy(void) { if (ph_table) { lsarpc_ph_clear_table(); g_hash_table_destroy(ph_table); ph_table = NULL; } g_free(domain_name); domain_name = NULL; } ksmbd-tools-3.5.2/mountd/rpc_samr.c000066400000000000000000000560251460410342700172500ustar00rootroot00000000000000// 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 GPtrArray *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); g_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 = g_try_malloc0(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; if (ndr_read_uniq_vstring_ptr(dce, &server_name)) return KSMBD_RPC_EINVALID_PARAMETER; ndr_free_uniq_vstring_ptr(&server_name); // Access mask if (ndr_read_int32(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; // level in if (ndr_read_int32(dce, &dce->sm_req.level)) return KSMBD_RPC_EINVALID_PARAMETER; // Info in if (ndr_read_int32(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; if (ndr_read_int32(dce, &dce->sm_req.client_version)) return KSMBD_RPC_EINVALID_PARAMETER; return 0; } static int samr_connect5_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; struct connect_handle *ch; if (ndr_write_union_int32(dce, dce->sm_req.level)) //level out return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, dce->sm_req.client_version)) //client version return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) //reserved return KSMBD_RPC_EBAD_DATA; ch = samr_ch_alloc(pipe->id); if (!ch) return KSMBD_RPC_ENOMEM; /* write connect handle */ if (ndr_write_bytes(dce, ch->handle, HANDLE_SIZE)) return KSMBD_RPC_EBAD_DATA; return KSMBD_RPC_OK; } static int samr_enum_domain_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; 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++) { char *entry; size_t name_len; ret = ndr_write_int32(dce, i); if (ret) return ret; entry = g_ptr_array_index(domain_entries, i); name_len = strlen(entry); ret = ndr_write_int16(dce, name_len*2); if (ret) return ret; ret = ndr_write_int16(dce, name_len*2); if (ret) return ret; /* ref pointer for name entry */ dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); if (ret) return ret; } for (i = 0; i < num_domain_entries; i++) { char *entry; entry = g_ptr_array_index(domain_entries, i); ret = ndr_write_string(dce, entry); if (ret) return ret; } return ret; } static int samr_enum_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; /* Resume Handle */ if (ndr_write_int32(dce, 0)) return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, num_domain_entries)) // Sam entry count return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, num_domain_entries)) // Sam max entry count return KSMBD_RPC_EBAD_DATA; if (samr_ndr_write_domain_array(pipe)) return KSMBD_RPC_EBAD_DATA; /* [out] DWORD* Num Entries */ if (ndr_write_int32(dce, num_domain_entries)) return KSMBD_RPC_EBAD_DATA; return KSMBD_RPC_OK; } static int samr_lookup_domain_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; // name len if (ndr_read_int16(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; // name size if (ndr_read_int16(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; // domain name if (ndr_read_uniq_vstring_ptr(dce, &dce->sm_req.name)) return KSMBD_RPC_EINVALID_PARAMETER; 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++; if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 4)) return KSMBD_RPC_EBAD_DATA; for (i = 0; i < num_domain_entries; i++) { char *entry; entry = g_ptr_array_index(domain_entries, i); if (!strcmp(STR_VAL(dce->sm_req.name), entry)) { smb_init_domain_sid(&sid); if (smb_write_sid(dce, &sid)) return KSMBD_RPC_EBAD_DATA; } } ndr_free_uniq_vstring_ptr(&dce->sm_req.name); return KSMBD_RPC_OK; } static int samr_open_domain_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; 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++; if (ndr_write_bytes(dce, ch->handle, HANDLE_SIZE)) return KSMBD_RPC_EBAD_DATA; return KSMBD_RPC_OK; } static int samr_lookup_names_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; int user_num; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; if (ndr_read_int32(dce, &user_num)) return KSMBD_RPC_EINVALID_PARAMETER; // max count if (ndr_read_int32(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; // offset if (ndr_read_int32(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; // actual count if (ndr_read_int32(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; // name len if (ndr_read_int16(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; // name size if (ndr_read_int16(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; // names if (ndr_read_uniq_vstring_ptr(dce, &dce->sm_req.name)) return KSMBD_RPC_EINVALID_PARAMETER; 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; if (ndr_write_int32(dce, 1)) // count return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 1)) // count return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, ch->user->uid)) // RID return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 1)) return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 1)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 1)) return KSMBD_RPC_EBAD_DATA; return KSMBD_RPC_OK; } static int samr_open_user_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; if (ndr_read_int32(dce, NULL)) return KSMBD_RPC_EINVALID_PARAMETER; // RID if (ndr_read_int32(dce, &dce->sm_req.rid)) return KSMBD_RPC_EINVALID_PARAMETER; 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 (!ch->user) return KSMBD_RPC_EBAD_FID; if (dce->sm_req.rid != ch->user->uid) return KSMBD_RPC_EBAD_FID; if (ndr_write_bytes(dce, ch->handle, HANDLE_SIZE)) return KSMBD_RPC_EBAD_DATA; return KSMBD_RPC_OK; } static int samr_query_user_info_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; 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; g_autofree char *profile_path = NULL; char hostname[NAME_MAX]; int home_dir_len, i, ret; ch = samr_ch_lookup(dce->sm_req.handle); if (!ch) return KSMBD_RPC_EBAD_FID; if (gethostname(hostname, NAME_MAX)) return KSMBD_RPC_ENOMEM; home_dir_len = 2 + strlen(hostname) + 1 + strlen(ch->user->name) + 1; home_dir = g_try_malloc0(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 = g_try_malloc0(home_dir_len + strlen("profile")); if (!profile_path) { g_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++; ret = ndr_write_int32(dce, dce->num_pointers); // ref pointer if (ret) goto out; ret = ndr_write_int16(dce, 0x15); // info if (ret) goto out; ret = ndr_write_int16(dce, 0); if (ret) goto out; /* * Last Logon/Logoff/Password change, Acct Expiry, * Allow Passworkd Change, Force Password Change. */ for (i = 0; i < 6; i++) { ret = ndr_write_int64(dce, 0); if (ret) goto out; } ret = ndr_write_int16(dce, strlen(ch->user->name)*2); // account name length if (ret) goto out; ret = ndr_write_int16(dce, strlen(ch->user->name)*2); if (ret) goto out; dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); // ref pointer if (ret) goto out; ret = ndr_write_int16(dce, strlen(ch->user->name)*2); // full name length if (ret) goto out; ret = ndr_write_int16(dce, strlen(ch->user->name)*2); if (ret) goto out; dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); // ref pointer if (ret) goto out; ret = ndr_write_int16(dce, strlen(home_dir)*2); // home directory length if (ret) goto out; ret = ndr_write_int16(dce, strlen(home_dir)*2); if (ret) goto out; /* Home Drive, Logon Script */ for (i = 0; i < 2; i++) { dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); // ref pointer if (ret) goto out; ret = ndr_write_int16(dce, 0); if (ret) goto out; ret = ndr_write_int16(dce, 0); if (ret) goto out; } dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); // ref pointer if (ret) goto out; ret = ndr_write_int16(dce, strlen(profile_path)*2); //profile path length if (ret) goto out; ret = ndr_write_int16(dce, strlen(profile_path)*2); if (ret) goto out; /* Description, Workstations, Comments, Parameters */ for (i = 0; i < 4; i++) { dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); // ref pointer if (ret) goto out; ret = ndr_write_int16(dce, 0); if (ret) goto out; ret = ndr_write_int16(dce, 0); if (ret) goto out; } dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); if (ret) goto out; /* Lm, Nt, Password and Private*/ for (i = 0; i < 3; i++) { ret = ndr_write_int16(dce, 0); if (ret) goto out; ret = ndr_write_int16(dce, 0); if (ret) goto out; ret = ndr_write_int32(dce, 0); if (ret) goto out; } ret = ndr_write_int32(dce, 0); // buf count if (ret) goto out; /* Pointer to Buffer */ ret = ndr_write_int32(dce, 0); if (ret) goto out; ret = ndr_write_int32(dce, ch->user->uid); // rid if (ret) goto out; ret = ndr_write_int32(dce, 513); // primary gid if (ret) goto out; ret = ndr_write_int32(dce, 0x00000010); // Acct Flags : Acb Normal if (ret) goto out; ret = ndr_write_int32(dce, 0x00FFFFFF); // Fields Present if (ret) goto out; ret = ndr_write_int16(dce, 168); // logon hours if (ret) goto out; ret = ndr_write_int16(dce, 0); if (ret) goto out; /* Pointers to Bits */ dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); //ref pointer if (ret) goto out; /* Bad Password/Logon Count/Country Code/Code Page */ for (i = 0; i < 4; i++) { ret = ndr_write_int16(dce, 0); if (ret) goto out; } /* Lm/Nt Password Set, Password Expired/etc */ ret = ndr_write_int8(dce, 0); if (ret) goto out; ret = ndr_write_int8(dce, 0); if (ret) goto out; ret = ndr_write_int8(dce, 0); if (ret) goto out; ret = ndr_write_int8(dce, 0); if (ret) goto out; ret = ndr_write_string(dce, ch->user->name); if (ret) goto out; ret = ndr_write_string(dce, ch->user->name); if (ret) goto out; ret = ndr_write_string(dce, home_dir); if (ret) goto out; /* Home Drive, Logon Script */ for (i = 0; i < 2; i++) { ret = ndr_write_int32(dce, 0); if (ret) goto out; ret = ndr_write_int32(dce, 0); if (ret) goto out; ret = ndr_write_int32(dce, 0); if (ret) goto out; } ret = ndr_write_string(dce, profile_path); if (ret) goto out; /* Description, Workstations, Comments, Parameters */ for (i = 0; i < 4; i++) { ret = ndr_write_int32(dce, 0); if (ret) goto out; ret = ndr_write_int32(dce, 0); if (ret) goto out; ret = ndr_write_int32(dce, 0); if (ret) goto out; } /* Logon Hours */ ret = ndr_write_int32(dce, 1260); if (ret) goto out; ret = ndr_write_int32(dce, 0); if (ret) goto out; ret = ndr_write_int32(dce, 21); if (ret) goto out; for (i = 0; i < 21; i++) { ret = ndr_write_int8(dce, 0xff); if (ret) break; } out: g_free(home_dir); return ret ? KSMBD_RPC_EBAD_DATA: KSMBD_RPC_OK; } static int samr_query_security_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; 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; if (!ch->user) return KSMBD_RPC_EBAD_FID; curr_offset = dce->offset; dce->offset += 16; if (build_sec_desc(dce, &sec_desc_len, ch->user->uid)) return KSMBD_RPC_EBAD_DATA; payload_offset = dce->offset; dce->offset = curr_offset; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, sec_desc_len)) return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, sec_desc_len)) return KSMBD_RPC_EBAD_DATA; 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; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EINVALID_PARAMETER; 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++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 1)) // count return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 1)) // max count return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 513)) // group rid return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0x00000007)) // attributes return KSMBD_RPC_EBAD_DATA; return KSMBD_RPC_OK; } static int samr_get_alias_membership_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EACCESS_DENIED; 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; if (ndr_write_int32(dce, 0)) // count return KSMBD_RPC_EBAD_DATA; dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) // ref pointer return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) // max count return KSMBD_RPC_EBAD_DATA; return KSMBD_RPC_OK; } static int samr_close_invoke(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_read_bytes(dce, dce->sm_req.handle, HANDLE_SIZE)) return KSMBD_RPC_EACCESS_DENIED; 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; if (ch->refcount > 1) ch->refcount--; else samr_ch_free(ch); /* write connect handle */ if (ndr_write_int64(dce, 0)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int64(dce, 0)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) return KSMBD_RPC_EBAD_DATA; 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 */ if (ndr_write_int32(dce, status)) return KSMBD_RPC_EBAD_DATA; if (dcerpc_write_headers(dce, status)) return KSMBD_RPC_EBAD_DATA; 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 void rpc_samr_add_domain_entry(char *name) { g_ptr_array_add(domain_entries, g_strdup(name)); num_domain_entries++; } void rpc_samr_init(void) { if (!domain_name) { char hostname[NAME_MAX]; /* * ksmbd supports the standalone server and * uses the hostname as the domain name. */ if (gethostname(hostname, NAME_MAX)) abort(); domain_name = g_ascii_strup(hostname, -1); } if (!domain_entries) { domain_entries = g_ptr_array_new_with_free_func(g_free); rpc_samr_add_domain_entry(domain_name); rpc_samr_add_domain_entry("Builtin"); } if (!ch_table) ch_table = g_hash_table_new(g_str_hash, g_str_equal); } static void samr_ch_clear_table(void) { struct connect_handle *ch; GHashTableIter iter; g_rw_lock_writer_lock(&ch_table_lock); ghash_for_each(ch, ch_table, iter) g_free(ch); g_rw_lock_writer_unlock(&ch_table_lock); } void rpc_samr_destroy(void) { if (ch_table) { samr_ch_clear_table(); g_hash_table_destroy(ch_table); ch_table = NULL; } if (domain_entries) { g_ptr_array_free(domain_entries, 1); domain_entries = NULL; } num_domain_entries = 0; g_free(domain_name); domain_name = NULL; } ksmbd-tools-3.5.2/mountd/rpc_srvsvc.c000066400000000000000000000272701460410342700176340ustar00rootroot00000000000000// 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; if (share->comment) 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 */ if (ret) return ret; ret = ndr_write_int32(dce, __share_type(share)); if (ret) return ret; dce->num_pointers++; return ndr_write_int32(dce, dce->num_pointers); /* ref pointer */ } 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); if (ret) return ret; return ndr_write_vstring(dce, share->comment); } 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 */ if (ret) return ret; ret = ndr_write_int32(dce, 0); if (ret) return ret; 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_ptr_array_remove_index(pipe->entries, i); pipe->num_entries--; pipe->num_processed++; put_ksmbd_share(share); return 0; } static void __enum_all_shares(struct ksmbd_share *share, struct ksmbd_rpc_pipe *pipe) { 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; } g_ptr_array_add(pipe->entries, share); pipe->num_entries++; } static int srvsvc_share_enum_all_invoke(struct ksmbd_rpc_pipe *pipe) { shm_iter_shares((share_cb)__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; share = shm_lookup_share(STR_VAL(hdr->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) { ret = shm_lookup_hosts_map(share, KSMBD_SHARE_HOSTS_DENY_MAP, STR_VAL(hdr->server_name)); if (!ret) { put_ksmbd_share(share); return 0; } } g_ptr_array_add(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; if (ndr_write_union_int32(dce, dce->si_req.level)) return KSMBD_RPC_EBAD_DATA; status = ndr_write_array_of_structs(pipe); if (status == KSMBD_RPC_EBAD_DATA) return status; /* * [out] DWORD* TotalEntries * [out, unique] DWORD* ResumeHandle */ if (ndr_write_int32(dce, pipe->num_processed)) return KSMBD_RPC_EBAD_DATA; if (status == KSMBD_RPC_EMORE_DATA) { dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0x01)) return KSMBD_RPC_EBAD_DATA; /* Have pending data, set RETURN_READY again */ dce->flags |= KSMBD_DCERPC_RETURN_READY; } else { dce->num_pointers++; if (ndr_write_int32(dce, dce->num_pointers)) return KSMBD_RPC_EBAD_DATA; if (ndr_write_int32(dce, 0)) return KSMBD_RPC_EBAD_DATA; } return status; } static int srvsvc_share_get_info_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_write_union_int32(dce, dce->si_req.level)) return KSMBD_RPC_EBAD_DATA; 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; } if (dce->entry_rep(dce, NULL)) return KSMBD_RPC_EBAD_DATA; 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) { if (ndr_read_uniq_vstring_ptr(dce, &hdr->server_name)) return -EINVAL; if (dce->req_hdr.opnum == SRVSVC_OPNUM_SHARE_ENUM_ALL) { int ptr; __u32 val; /* Read union switch selector */ if (ndr_read_union_int32(dce, &val)) return -EINVAL; hdr->level = val; // read container pointer ref id if (ndr_read_int32(dce, NULL)) return -EINVAL; // read container array size if (ndr_read_int32(dce, NULL)) return -EINVAL; // read container array pointer if (ndr_read_int32(dce, &ptr)) return -EINVAL; // it should be null if (ptr != 0x00) { pr_err("SRVSVC: container array pointer is %x\n", ptr); return -EINVAL; } if (ndr_read_int32(dce, &val)) return -EINVAL; hdr->max_size = val; if (ndr_read_uniq_ptr(dce, &hdr->payload_handle)) return -EINVAL; return 0; } if (dce->req_hdr.opnum == SRVSVC_OPNUM_GET_SHARE_INFO) { if (ndr_read_vstring_ptr(dce, &hdr->share_name)) return -EINVAL; if (ndr_read_int32(dce, &hdr->level)) return -EINVAL; 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_vstring_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 */ if (ndr_write_int32(dce, status)) return KSMBD_RPC_EBAD_DATA; if (dcerpc_write_headers(dce, status)) return KSMBD_RPC_EBAD_DATA; 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.5.2/mountd/rpc_wkssvc.c000066400000000000000000000125201460410342700176160ustar00rootroot00000000000000// 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_vstring_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); if (ret) return ret; /* server_name */ dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); /* ref pointer */ if (ret) return ret; /* domain_name */ dce->num_pointers++; ret = ndr_write_int32(dce, dce->num_pointers); /* ref pointer */ if (ret) return ret; /* version_major */ ret = ndr_write_int32(dce, WKSSVC_VERSION_MAJOR); if (ret) return ret; /* 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)); if (ret) return ret; return ndr_write_vstring(dce, global_conf.work_group); } static int wkssvc_netwksta_get_info_return(struct ksmbd_rpc_pipe *pipe) { struct ksmbd_dcerpc *dce = pipe->dce; if (ndr_write_union_int32(dce, dce->wi_req.level)) return KSMBD_RPC_EBAD_DATA; 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; } if (dce->entry_rep(dce, NULL)) return KSMBD_RPC_EBAD_DATA; if (dce->entry_data(dce, NULL)) return KSMBD_RPC_EBAD_DATA; 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 */ if (ndr_write_int32(dce, status)) return KSMBD_RPC_EBAD_DATA; if (dcerpc_write_headers(dce, status)) return KSMBD_RPC_EBAD_DATA; 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) { int val; if (ndr_read_uniq_vstring_ptr(dce, &hdr->server_name)) return -EINVAL; if (ndr_read_int32(dce, &val)) return -EINVAL; hdr->level = val; 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.5.2/mountd/smbacl.c000066400000000000000000000174441460410342700167050ustar00rootroot00000000000000// 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} }; int smb_read_sid(struct ksmbd_dcerpc *dce, struct smb_sid *sid) { int i; if (ndr_read_int8(dce, &sid->revision)) return -EINVAL; if (ndr_read_int8(dce, &sid->num_subauth)) return -EINVAL; if (!sid->num_subauth || sid->num_subauth >= SID_MAX_SUB_AUTHORITIES) return -EINVAL; for (i = 0; i < NUM_AUTHS; ++i) if (ndr_read_int8(dce, &sid->authority[i])) return -EINVAL; for (i = 0; i < sid->num_subauth; ++i) if (ndr_read_int32(dce, &sid->sub_auth[i])) return -EINVAL; return 0; } int smb_write_sid(struct ksmbd_dcerpc *dce, const struct smb_sid *src) { int i; if (ndr_write_int8(dce, src->revision)) return -ENOMEM; if (ndr_write_int8(dce, src->num_subauth)) return -ENOMEM; for (i = 0; i < NUM_AUTHS; ++i) { if (ndr_write_int8(dce, src->authority[i])) return -ENOMEM; } for (i = 0; i < src->num_subauth; ++i) { if (ndr_write_int32(dce, src->sub_auth[i])) return -ENOMEM; } return 0; } 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; 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; 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; return -1; } } } return 0; /* sids compare/match */ } static int smb_sid_to_string(char *domain, size_t domain_len, struct smb_sid *sid) { int i, len; len = snprintf(domain, domain_len, "S-%i-%i", (int)sid->revision, (int)sid->authority[5]); if (len < 0 || len > domain_len) return -ENOMEM; for (i = 0; i < sid->num_subauth; i++) { len += snprintf(domain + len, domain_len - len, "-%u", sid->sub_auth[i]); if (len < 0 || len > domain_len) return -ENOMEM; } return len; } int set_domain_name(struct smb_sid *sid, char *domain, size_t domain_len, int *type) { int ret = 0; char domain_string[DOMAIN_STR_SIZE] = {0}; g_autofree char *domain_name = NULL; if (!smb_compare_sids(sid, &sid_domain) && !memcmp(&sid->sub_auth[1], global_conf.gen_subauth, sizeof(__u32) * 3)) { if (gethostname(domain_string, DOMAIN_STR_SIZE)) return -ENOMEM; domain_name = g_ascii_strup(domain_string, -1); if (!domain_name) return -ENOMEM; ret = snprintf(domain, domain_len, "%s", domain_name); if (ret < 0 || ret >= domain_len) return -ENOMEM; *type = SID_TYPE_USER; } else if (!smb_compare_sids(sid, &sid_unix_users)) { ret = snprintf(domain, domain_len, "Unix User"); if (ret < 0 || ret >= domain_len) return -ENOMEM; *type = SID_TYPE_USER; } else if (!smb_compare_sids(sid, &sid_unix_groups)) { ret = snprintf(domain, domain_len, "Unix Group"); if (ret < 0 || ret >= domain_len) return -ENOMEM; *type = SID_TYPE_GROUP; } else { ret = smb_sid_to_string(domain_string, sizeof(domain_string), sid); if (ret < 0) return ret; if (ret > domain_len) return -ENOMEM; domain_name = g_ascii_strup(domain_string, -1); if (!domain_name) return -ENOMEM; ret = snprintf(domain, domain_len, "%s", domain_name); if (ret < 0 || ret >= domain_len) return -ENOMEM; *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)); // ace type if (ndr_write_int8(dce, ACCESS_ALLOWED)) return -ENOMEM; // ace flags if (ndr_write_int8(dce, 0)) return -ENOMEM; size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid.num_subauth * 4); if (rid) size += 4; // ace size if (ndr_write_int16(dce, size)) return -ENOMEM; // ace access required if (ndr_write_int32(dce, access_req)) return -ENOMEM; if (rid) sid.sub_auth[sid.num_subauth++] = rid; if (smb_write_sid(dce, &sid)) return -ENOMEM; return size; } static int set_dacl(struct ksmbd_dcerpc *dce, int rid) { int size = 0, i, ret; struct smb_sid owner_domain; /* Other */ ret = smb_set_ace(dce, 0x0002035b, 0, &sid_everyone); if (ret < 0) return ret; size += ret; /* Local Group Administrators */ ret = smb_set_ace(dce, 0x000f07ff, 544, &sid_local_group); if (ret < 0) return ret; size += ret; /* Local Group Account Operators */ ret = smb_set_ace(dce, 0x000f07ff, 548, &sid_local_group); if (ret < 0) return ret; size += ret; /* 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++; } ret = smb_set_ace(dce, 0x00020044, rid, &owner_domain); if (ret < 0) return ret; size += ret; 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 Descriptor : Revision */ if (ndr_write_int16(dce, 1)) return -ENOMEM; /* ACL Type */ if (ndr_write_int16(dce, SELF_RELATIVE | DACL_PRESENT)) return -ENOMEM; /* Offset to owner SID */ if (ndr_write_int32(dce, 0)) return -ENOMEM; /* Offset to group SID */ if (ndr_write_int32(dce, 0)) return -ENOMEM; /* Offset to SACL */ if (ndr_write_int32(dce, 0)) return -ENOMEM; /* Offset to DACL */ if (ndr_write_int32(dce, sizeof(struct smb_ntsd))) return -ENOMEM; /* DACL Revision */ if (ndr_write_int16(dce, 2)) return -ENOMEM; acl_size_offset = dce->offset; dce->offset += 2; /* Number of ACEs */ if (ndr_write_int32(dce, 4)) return -ENOMEM; acl_size = set_dacl(dce, rid) + sizeof(struct smb_acl); if (acl_size < 0) return -ENOMEM; /* ACL Size */ l_offset = dce->offset; dce->offset = acl_size_offset; if (ndr_write_int16(dce, acl_size)) return -ENOMEM; dce->offset = l_offset; *secdesclen = sizeof(struct smb_ntsd) + acl_size; return 0; } ksmbd-tools-3.5.2/mountd/worker.c000066400000000000000000000175471460410342700167610ustar00rootroot00000000000000// 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("Unable to login user `%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: g_free(auth_out.spnego_blob); g_free(auth_out.sess_key); g_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); resp->payload_sz = payload_sz; 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 = NULL; int ret = -ENOTSUP; if (msg->sz < sizeof(struct ksmbd_rpc_command)) goto out; 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); pool = NULL; } } void wp_init(void) { if (!pool) pool = g_thread_pool_new( worker_pool_fn, NULL, MAX_WORKER_THREADS, 0, NULL); } ksmbd-tools-3.5.2/tools/000077500000000000000000000000001460410342700151205ustar00rootroot00000000000000ksmbd-tools-3.5.2/tools/Makefile.am000066400000000000000000000017511460410342700171600ustar00rootroot00000000000000AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBKRB5_CFLAGS) -fno-common LIBS = $(GLIB_LIBS) $(LIBNL_LIBS) $(LIBKRB5_LIBS) $(PTHREAD_LIBS) EXTRA_DIST = meson.build libexec_PROGRAMS = ksmbd.tools ksmbd_tools_SOURCES = management/tree_conn.c \ management/user.c \ management/share.c \ management/session.c \ config_parser.c \ tools.c if HAVE_LIBKRB5 ksmbd_tools_SOURCES += management/spnego.c \ asn1.c \ management/spnego_krb5.c \ management/spnego_mech.h endif ksmbd_tools_LDADD = $(top_builddir)/addshare/libaddshare.a \ $(top_builddir)/adduser/libadduser.a \ $(top_builddir)/control/libcontrol.a \ $(top_builddir)/mountd/libmountd.a ksmbd-tools-3.5.2/tools/asn1.c000066400000000000000000000165771460410342700161460ustar00rootroot00000000000000// 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 #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 = g_try_malloc(eoc - ctx->pointer); if (!*octets) return 0; ptr = *octets; while (ctx->pointer < eoc) { if (!asn1_octet_decode(ctx, (unsigned char *) ptr++)) { g_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 = g_try_malloc(len); if (!*buf) return 0; 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 = g_try_malloc0_n(size, sizeof(unsigned long)); if (*oid == NULL) return 0; optr = *oid; if (!asn1_subid_decode(ctx, &subid)) { g_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; g_free(*oid); *oid = NULL; return 0; } if (!asn1_subid_decode(ctx, optr++)) { g_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 = g_try_malloc0_n(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.5.2/tools/config_parser.c000066400000000000000000000471451460410342700201200ustar00rootroot00000000000000// 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; typedef int process_entry_fn(char *entry); static process_entry_fn process_smbconf_entry, process_pwddb_entry, process_subauth_entry, process_lock_entry; unsigned long long cp_memparse(char *v) { char *cp; unsigned long long ull = strtoull(v, &cp, 0); switch (*cp) { case 'E': case 'e': ull <<= 10; /* Fall through */ case 'P': case 'p': ull <<= 10; /* Fall through */ case 'T': case 't': ull <<= 10; /* Fall through */ case 'G': case 'g': ull <<= 10; /* Fall through */ case 'M': case 'm': ull <<= 10; /* Fall through */ case 'K': case 'k': ull <<= 10; } return ull; } static int is_ascii_space_tab(char c) { return c == ' ' || c == '\t'; } static int is_a_group(char *entry) { char *delim; int is_group; is_group = *entry == '['; if (!is_group) goto out; entry++; delim = strchr(entry, ']'); is_group = shm_share_name(entry, delim); if (!is_group) goto out; entry = cp_ltrim(delim + 1); is_group = cp_smbconf_eol(entry); if (!is_group) { pr_debug("Group contains `%c' [0x%.2X]\n", *entry, (unsigned char)*entry); goto out; } *entry = 0x00; out: return is_group; } static int is_a_key_value(char *entry) { char *delim; int is_key_value; delim = strchr(entry, '='); is_key_value = delim > entry; if (!is_key_value) goto out; for (; entry < delim; entry++) { is_key_value = cp_printable(entry) && !cp_smbconf_eol(entry); if (!is_key_value) { pr_debug("Key contains `%c' [0x%.2X]\n", *entry, (unsigned char)*entry); goto out; } } is_key_value = !!parser.current; if (!is_key_value) { pr_debug("Key has no group\n"); goto out; } entry = cp_ltrim(entry + 1); for (; !cp_smbconf_eol(entry); entry++) { is_key_value = cp_printable(entry) || *entry == '\t'; if (!is_key_value) { pr_debug("Value contains `%c' [0x%.2X]\n", *entry, (unsigned char)*entry); goto out; } } *entry = 0x00; out: return is_key_value; } static void add_group(const char *entry) { g_autofree char *name = g_strndup(entry + 1, strchr(entry, ']') - entry - 1); struct smbconf_group *g = g_hash_table_lookup(parser.groups, name); if (g) { parser.current = g; return; } g = g_malloc(sizeof(struct smbconf_group)); g->name = name; g->kv = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); g_hash_table_insert(parser.groups, name, g); name = NULL; parser.current = g; if (!parser.global && shm_share_name_equal(g->name, "global")) parser.global = g; else if (!parser.ipc && shm_share_name_equal(g->name, "ipc$")) parser.ipc = g; } static void add_group_key_value(const char *entry) { const char *delim = strchr(entry, '='); g_autofree char *k = g_strndup(entry, cp_rtrim(entry, delim - 1) + 1 - entry); g_autofree char *v = g_strdup(cp_ltrim(delim + 1)); if (cp_smbconf_eol(v) || g_hash_table_lookup(parser.current->kv, k)) return; g_hash_table_insert(parser.current->kv, k, v); k = v = NULL; } static int process_smbconf_entry(char *entry) { entry = cp_ltrim(entry); if (cp_smbconf_eol(entry)) return 0; if (is_a_group(entry)) { add_group(entry); return 0; } if (is_a_key_value(entry)) { add_group_key_value(entry); return 0; } pr_err("Invalid smbconf entry `%s'\n", entry); return -EINVAL; } static int __mmap_parse_file(const char *path, process_entry_fn *process_entry) { GError *error = NULL; GMappedFile *file; char *contents, *delim; size_t len; int fd, ret; fd = open(path, O_RDONLY); if (fd < 0) { ret = -errno; pr_debug("Can't open `%s': %m\n", path); goto out; } file = g_mapped_file_new_from_fd(fd, 0, &error); if (error) { pr_err("%s\n", error->message); g_error_free(error); ret = -EINVAL; goto out_close; } contents = g_mapped_file_get_contents(file); if (!contents) { g_autofree char *entry = g_strdup(""); ret = process_entry(entry); goto out_unref; } for (len = g_mapped_file_get_length(file); len > 0 && len != (size_t)-1; len -= delim - contents + 1, contents = delim + 1) { g_autofree char *entry = NULL; delim = memchr(contents, '\n', len) ?: contents + len; entry = g_strndup(contents, delim - contents); ret = process_entry(entry); if (ret || process_entry == process_subauth_entry || process_entry == process_lock_entry) goto out_unref; } out_unref: g_mapped_file_unref(file); out_close: close(fd); out: return ret; } static void free_group(struct smbconf_group *g) { g_hash_table_destroy(g->kv); g_free(g->name); g_free(g); } char *cp_ltrim(const char *v) { while (is_ascii_space_tab(*v)) v++; return (char *)v; } char *cp_rtrim(const char *v, const char *p) { while (p != v && is_ascii_space_tab(*p)) p--; return (char *)p; } int cp_key_cmp(const char *lk, const char *rk) { return g_ascii_strncasecmp(lk, rk, strlen(rk)); } char *cp_get_group_kv_string(char *v) { return g_strdup(v); } int cp_get_group_kv_bool(char *v) { return !cp_key_cmp(v, "yes") || !cp_key_cmp(v, "1") || !cp_key_cmp(v, "true") || !cp_key_cmp(v, "enable"); } int cp_get_group_kv_config_opt(char *v) { if (!cp_key_cmp(v, "enabled")) return KSMBD_CONFIG_OPT_ENABLED; if (!cp_key_cmp(v, "auto")) return KSMBD_CONFIG_OPT_AUTO; if (!cp_key_cmp(v, "mandatory")) 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) { return g_strsplit_set(v, "\t ,", -1); } void cp_group_kv_list_free(char **list) { g_strfreev(list); } static void process_global_conf_kv(char *k, char *v) { 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")) { global_conf.guest_account = cp_get_group_kv_string(v); return; } if (!cp_key_cmp(k, "max active sessions")) { global_conf.sessions_cap = cp_get_group_kv_long(v); if (global_conf.sessions_cap <= 0 || global_conf.sessions_cap > KSMBD_CONF_MAX_ACTIVE_SESSIONS) global_conf.sessions_cap = KSMBD_CONF_MAX_ACTIVE_SESSIONS; return; } if (!cp_key_cmp(k, "tcp port")) { /* mountd option has precedence */ 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); if (!global_conf.file_max || global_conf.file_max > KSMBD_CONF_MAX_OPEN_FILES) global_conf.file_max = KSMBD_CONF_MAX_OPEN_FILES; 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_1 && global_conf.restrict_anon != KSMBD_RESTRICT_ANON_TYPE_2) global_conf.restrict_anon = 0; return; } if (!cp_key_cmp(k, "map to guest")) { if (!cp_key_cmp(v, "bad user")) global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_BAD_USER; /* broken */ #if 0 else if (!cp_key_cmp(v, "bad password")) global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD; else if (!cp_key_cmp(v, "bad uid")) global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_BAD_UID; #endif else global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_NEVER; 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 = cp_memparse(v); return; } if (!cp_key_cmp(k, "smb2 max write")) { global_conf.smb2_max_write = cp_memparse(v); return; } if (!cp_key_cmp(k, "smb2 max trans")) { global_conf.smb2_max_trans = cp_memparse(v); return; } if (!cp_key_cmp(k, "smb3 encryption")) { switch (cp_get_group_kv_config_opt(v)) { case KSMBD_CONFIG_OPT_DISABLED: global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION_OFF; global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION; break; case KSMBD_CONFIG_OPT_MANDATORY: global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION; global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION_OFF; break; default: global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION; global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION_OFF; break; } 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 = cp_memparse(v); return; } if (!cp_key_cmp(k, "smbd max io size")) { global_conf.smbd_max_io_size = cp_memparse(v); return; } if (!cp_key_cmp(k, "max connections")) { global_conf.max_connections = cp_memparse(v); if (!global_conf.max_connections || global_conf.max_connections > KSMBD_CONF_MAX_CONNECTIONS) global_conf.max_connections = KSMBD_CONF_MAX_CONNECTIONS; return; } if (!cp_key_cmp(k, "durable handles")) { if (cp_get_group_kv_bool(v)) global_conf.flags |= KSMBD_GLOBAL_FLAG_DURABLE_HANDLES; else global_conf.flags &= ~KSMBD_GLOBAL_FLAG_DURABLE_HANDLES; return; } } static void add_group_global_conf(void) { char *k, *v; GHashTableIter iter; if (ksmbd_health_status & KSMBD_SHOULD_RELOAD_CONFIG) return; add_group_key_value("guest account = nobody"); add_group_key_value("max active sessions = 1024"); add_group_key_value("max connections = 128"); add_group_key_value("max open files = 10000"); add_group_key_value("netbios name = KSMBD SERVER"); add_group_key_value("server string = SMB SERVER"); add_group_key_value("share:fake_fscaps = 64"); /* sparse files */ add_group_key_value("tcp port = 445"); add_group_key_value("workgroup = WORKGROUP"); ghash_for_each(k, v, parser.current->kv, iter) process_global_conf_kv(k, v); } static void add_group_global_share_conf(void) { enum KSMBD_SHARE_CONF c; for (c = 0; c < KSMBD_SHARE_CONF_MAX; c++) { const char *k = KSMBD_SHARE_CONF[c], *v = NULL; g_autofree char *entry = NULL; if (!KSMBD_SHARE_CONF_IS_GLOBAL(c)) v = g_hash_table_lookup(parser.global->kv, k); if (!v) v = KSMBD_SHARE_DEFCONF[c]; entry = g_strdup_printf("%s = %s", k, v); add_group_key_value(entry); } } static void add_group_ipc_share_conf(void) { add_group_key_value("comment = IPC share"); add_group_key_value("guest ok = yes"); } static int finalize_smbconf_parser(void) { int ret; GHashTableIter iter; add_group("[global]"); add_group_global_conf(); ret = usm_add_guest_account(global_conf.guest_account); if (ret) goto out; add_group("[ipc$]"); add_group_ipc_share_conf(); ghash_for_each_steal(parser.current, parser.groups, iter) { if (parser.current == parser.global) continue; add_group_global_share_conf(); shm_add_new_share(parser.current); free_group(parser.current); } free_group(parser.global); out: cp_smbconf_parser_destroy(); return ret; } void cp_smbconf_parser_init(void) { if (!parser.groups) parser.groups = g_hash_table_new_full( (GHashFunc)shm_share_name_hash, (GEqualFunc)shm_share_name_equal, NULL, (GDestroyNotify)free_group); } void cp_smbconf_parser_destroy(void) { if (parser.groups) { g_hash_table_destroy(parser.groups); parser.groups = NULL; } parser.current = parser.global = parser.ipc = NULL; } int cp_parse_smbconf(char *smbconf) { int is_owner = !parser.groups, ret; cp_smbconf_parser_init(); ret = __mmap_parse_file(smbconf, process_smbconf_entry); if (ret == -ENOENT) { if (TOOL_IS_ADDSHARE) { ret = set_conf_contents(smbconf, ""); } else if (TOOL_IS_MOUNTD) { pr_err("No configuration file\n"); } else { ret = 0; pr_info("No configuration file\n"); } } if (ret) { if (is_owner) cp_smbconf_parser_destroy(); return ret; } if (!is_owner) { struct smbconf_parser backup = parser; parser = (struct smbconf_parser){0}; ret = cp_parse_smbconf(smbconf); parser = backup; return ret; } return finalize_smbconf_parser(); } static int is_a_user_password(char *entry) { char *delim; int is_user_password; delim = strchr(entry, ':'); is_user_password = usm_user_name(entry, delim); if (!is_user_password) goto out; entry = delim + 1; for (; !cp_pwddb_eol(delim); delim++) ; *delim = 0x00; for (; delim > entry; delim--) if (delim[-1] != '=') break; is_user_password = delim > entry; if (!is_user_password) { pr_debug("Password is missing\n"); goto out; } for (; entry < delim; entry++) { is_user_password = (*entry >= '0' && *entry <= '9') || (*entry >= 'A' && *entry <= 'Z') || (*entry >= 'a' && *entry <= 'z') || (*entry == '+') || (*entry == '/'); if (!is_user_password) { pr_debug("Password contains `%c' [0x%.2X]\n", *entry, (unsigned char)*entry); goto out; } } out: return is_user_password; } static void add_user_password(const char *entry) { const char *delim = strchr(entry, ':'); g_autofree char *name = g_strndup(entry, delim - entry); g_autofree char *pwd = g_strdup(delim + 1); struct ksmbd_user *user = usm_lookup_user(name); if (user) { usm_update_user_password(user, pwd); put_ksmbd_user(user); return; } usm_add_new_user(name, pwd); name = pwd = NULL; } static int process_pwddb_entry(char *entry) { if (cp_pwddb_eol(entry)) return 0; if (is_a_user_password(entry)) { add_user_password(entry); return 0; } pr_err("Invalid pwddb entry `%s'\n", entry); return -EINVAL; } int cp_parse_pwddb(char *pwddb) { int ret = __mmap_parse_file(pwddb, process_pwddb_entry); if (ret == -ENOENT) { if (TOOL_IS_ADDUSER) { ret = set_conf_contents(pwddb, ""); } else { ret = 0; pr_info("No user database\n"); } } return ret; } static int is_a_subauth(char *entry) { int num_subauth = ARRAY_SIZE(global_conf.gen_subauth), is_subauth = 0; int i; for (i = 0; i < num_subauth; i++) { char *delim = strchr(entry, i + 1 < num_subauth ? ':' : 0x00); is_subauth = delim > entry; if (!is_subauth) { pr_debug("Subauth is missing\n"); goto out; } for (; entry < delim; entry++) { is_subauth = *entry >= '0' && *entry <= '9'; if (!is_subauth) { pr_debug("Subauth contains `%c' [0x%.2X]\n", *entry, (unsigned char)*entry); goto out; } } entry++; } out: return is_subauth; } static void add_subauth(const char *entry) { int num_subauth = ARRAY_SIZE(global_conf.gen_subauth); int i; for (i = 0; i < num_subauth; i++) { const char *delim = strchr(entry, i + 1 < num_subauth ? ':' : 0x00); global_conf.gen_subauth[i] = 0; for (; entry < delim; entry++) { global_conf.gen_subauth[i] *= 10; global_conf.gen_subauth[i] += *entry - '0'; } entry++; } } static int process_subauth_entry(char *entry) { if (is_a_subauth(entry)) { add_subauth(entry); return 0; } pr_err("Invalid subauth entry `%s'\n", entry); return -EINVAL; } int cp_parse_subauth(void) { int ret = __mmap_parse_file(PATH_SUBAUTH, process_subauth_entry); if (!TOOL_IS_MOUNTD) return ret; if (ret) { g_autofree char *contents = NULL; g_autoptr(GRand) rand = g_rand_new(); int num_subauth = ARRAY_SIZE(global_conf.gen_subauth); int i; for (i = 0; i < num_subauth; i++) { char *new_contents = g_strdup_printf( "%s%u%c", contents ?: "", g_rand_int(rand), i + 1 < num_subauth ? ':' : '\n'); g_free(contents); contents = new_contents; } if (ret == -ENOENT) ret = set_conf_contents(PATH_SUBAUTH, contents); *strchr(contents, '\n') = 0x00; add_subauth(contents); } return ret; } static int is_a_lock(char *entry) { char *delim = strchr(entry, 0x00); int is_lock; pid_t pid; is_lock = delim > entry; if (!is_lock) { pr_debug("Lock is missing\n"); goto out; } pid = 0; for (; entry < delim; entry++) { is_lock = *entry >= '0' && *entry <= '9'; if (!is_lock) { pr_debug("Lock contains `%c' [0x%.2X]\n", *entry, (unsigned char)*entry); goto out; } pid *= 10; pid += *entry - '0'; } is_lock = pid > 1; if (!is_lock) { pr_debug("Lock has invalid PID\n"); goto out; } is_lock = !kill(pid, 0); if (!is_lock) pr_debug("Lock has orphaned PID\n"); out: return is_lock; } static void add_lock(const char *entry) { global_conf.pid = 0; for (; *entry; entry++) { global_conf.pid *= 10; global_conf.pid += *entry - '0'; } } static int process_lock_entry(char *entry) { if (is_a_lock(entry)) { if (!TOOL_IS_MOUNTD) { add_lock(entry); return 0; } pr_err("Reserved lock entry `%s'\n", entry); return -EAGAIN; } pr_err("Invalid lock entry `%s'\n", entry); return -EINVAL; } int cp_parse_lock(void) { int ret = __mmap_parse_file(PATH_LOCK, process_lock_entry); if (!TOOL_IS_MOUNTD) return ret; if (ret) { g_autofree char *contents = g_strdup_printf("%d\n", getpid()); if (ret == -ENOENT || ret == -EINVAL) ret = set_conf_contents(PATH_LOCK, contents); *strchr(contents, '\n') = 0x00; add_lock(contents); } return ret; } void cp_parse_external_smbconf_group(char *name, char **options) { g_autofree char *group_name = g_strdup_printf("[%s]", name); int is_global; add_group(group_name); is_global = parser.current == parser.global; for (; *options; options++) { char *option = cp_ltrim(*options); if (cp_smbconf_eol(option)) continue; if (is_a_key_value(option)) { enum KSMBD_SHARE_CONF c; for (c = 0; c < KSMBD_SHARE_CONF_MAX; c++) if ((!is_global || !KSMBD_SHARE_CONF_IS_GLOBAL(c)) && shm_share_config(option, c)) break; if (c < KSMBD_SHARE_CONF_MAX) { g_hash_table_remove(parser.current->kv, KSMBD_SHARE_CONF[c]); add_group_key_value(option); continue; } } pr_info("Ignored option `%s'\n", option); } } ksmbd-tools-3.5.2/tools/management/000077500000000000000000000000001460410342700172345ustar00rootroot00000000000000ksmbd-tools-3.5.2/tools/management/session.c000066400000000000000000000112411460410342700210620ustar00rootroot00000000000000// 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 "tools.h" static GHashTable *sessions_table; static GRWLock sessions_table_lock; static void kill_ksmbd_session(struct ksmbd_session *sess) { g_list_free_full(sess->tree_conns, (GDestroyNotify)tcm_tree_conn_free); g_rw_lock_clear(&sess->update_lock); g_free(sess); } static struct ksmbd_session *new_ksmbd_session(unsigned long long id, struct ksmbd_user *user) { struct ksmbd_session *sess; sess = g_try_malloc0(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 sm_clear_sessions(void) { struct ksmbd_session *sess; GHashTableIter iter; ghash_for_each(sess, sessions_table, iter) kill_ksmbd_session(sess); } 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 int lookup_tree_conn(const struct ksmbd_tree_conn *tree_conn, const struct ksmbd_tree_conn *dummy) { return !(tree_conn->id == dummy->id); } 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, (GCompareFunc)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_session(sess); return 0; } void sm_destroy(void) { if (sessions_table) { sm_clear_sessions(); g_hash_table_destroy(sessions_table); sessions_table = NULL; } } void sm_init(void) { if (!sessions_table) sessions_table = g_hash_table_new(g_int64_hash, g_int64_equal); } ksmbd-tools-3.5.2/tools/management/share.c000066400000000000000000000477531460410342700205220ustar00rootroot00000000000000// 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 "config_parser.h" #include "linux/ksmbd_server.h" #include "management/share.h" #include "management/user.h" #include "tools.h" #define KSMBD_SHARE_STATE_FREEING 1 /* * WARNING: * * This must match KSMBD_SHARE_CONF enum 1:1. * Add new entries ONLY to the bottom. */ const char *KSMBD_SHARE_CONF[KSMBD_SHARE_CONF_MAX] = { /*0*/ "comment", "path", "guest ok", "guest account", "read only", /*5*/ "browseable", "write ok", "writeable", "store dos attributes", "oplocks", /*10*/ "create mask", "directory mask", "force create mode", "force directory mode", "force group", /*15*/ "force user", "hide dot files", "valid users", "invalid users", "read list", /*20*/ "write list", "admin users", "hosts allow", "hosts deny", "max connections", /*25*/ "veto files", "inherit owner", "follow symlinks", "vfs objects", "writable", /*30*/ "crossmnt", }; /* * WARNING: * * Same as above. * Know that entries may have COUPLING, e.g. they may be synonyms or antonyms. */ const char *KSMBD_SHARE_DEFCONF[KSMBD_SHARE_CONF_MAX] = { /*0*/ "", "", "no", "", "; yes", /*5*/ "yes", "", "", "yes", "yes", /*10*/ "0744", "0755", "0000", "0000", "", /*15*/ "", "yes", "", "", "", /*20*/ "", "", "", "", "128", /*25*/ "", "no", "no", "", "", /*30*/ "yes", }; static GHashTable *shares_table; static GRWLock shares_table_lock; int shm_share_name(char *name, char *p) { int is_name; is_name = p > name; if (!is_name) { pr_debug("Share name is missing\n"); goto out; } is_name = p - name < KSMBD_REQ_MAX_SHARE_NAME; if (!is_name) { pr_debug("Share name exceeds %d bytes\n", KSMBD_REQ_MAX_SHARE_NAME - 1); goto out; } is_name = g_utf8_validate(name, p - name, NULL); if (!is_name) { pr_debug("Share name is not UTF-8\n"); goto out; } for (; name < p; name++) { is_name = cp_printable(name) && *name != '[' && *name != ']'; if (!is_name) { pr_debug("Share name contains `%c' [0x%.2X]\n", *name, (unsigned char)*name); goto out; } } out: return is_name; } int shm_share_config(const char *k, enum KSMBD_SHARE_CONF c) { return !KSMBD_SHARE_CONF_IS_BROKEN(c) && !cp_key_cmp(k, KSMBD_SHARE_CONF[c]); } static void free_hosts_map(GHashTable *map) { if (map) { g_hash_table_destroy(map); } } static void free_user_map(GHashTable *map) { if (map) { struct ksmbd_user *user; GHashTableIter iter; ghash_for_each(user, map, iter) put_ksmbd_user(user); g_hash_table_destroy(map); } } static void kill_ksmbd_share(struct ksmbd_share *share) { int i; pr_debug("Kill share `%s' [0x%" PRIXPTR "]\n", share->name, (uintptr_t)share); 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); g_free(share->name); g_free(share->path); g_free(share->comment); g_free(share->veto_list); g_free(share->guest_account); g_rw_lock_clear(&share->update_lock); g_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); } void shm_remove_all_shares(void) { struct ksmbd_share *share; GHashTableIter iter; g_rw_lock_writer_lock(&shares_table_lock); ghash_for_each_remove(share, shares_table, iter) { share->state = KSMBD_SHARE_STATE_FREEING; put_ksmbd_share(share); } g_rw_lock_writer_unlock(&shares_table_lock); } static struct ksmbd_share *new_ksmbd_share(void) { struct ksmbd_share *share; int i; share = g_try_malloc0(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); share->force_uid = KSMBD_SHARE_INVALID_UID; share->force_gid = KSMBD_SHARE_INVALID_GID; if (ksmbd_health_status & KSMBD_SHOULD_RELOAD_CONFIG) set_share_flag(share, KSMBD_SHARE_FLAG_UPDATE); /* `available' share parameter does not exist yet */ set_share_flag(share, KSMBD_SHARE_FLAG_AVAILABLE); return share; } static void shm_clear_shares(void) { struct ksmbd_share *share; GHashTableIter iter; ghash_for_each(share, shares_table, iter) kill_ksmbd_share(share); } void shm_destroy(void) { if (shares_table) { shm_clear_shares(); g_hash_table_destroy(shares_table); shares_table = NULL; } } static char *shm_casefold_share_name(const char *name, size_t len) { g_autofree char *nfdi_name = NULL; nfdi_name = g_utf8_normalize(name, len, G_NORMALIZE_NFD); if (!nfdi_name) return g_ascii_strdown(name, len); return g_utf8_casefold(nfdi_name, -1); } unsigned int shm_share_name_hash(const char *name) { g_autofree char *cf_name = shm_casefold_share_name(name, -1); return g_str_hash(cf_name); } int shm_share_name_equal(const char *lname, const char *rname) { g_autofree char *cf_lname = shm_casefold_share_name(lname, -1); g_autofree char *cf_rname = shm_casefold_share_name(rname, -1); return g_str_equal(cf_lname, cf_rname); } void shm_init(void) { if (!shares_table) shares_table = g_hash_table_new( (GHashFunc)shm_share_name_hash, (GEqualFunc)shm_share_name_equal); } 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 void add_users_map(struct ksmbd_share *share, enum share_users map, char **names) { char **pp; if (!share->maps[map]) share->maps[map] = g_hash_table_new(g_str_hash, g_str_equal); for (pp = names; *pp; g_free(*pp++)) { struct ksmbd_user *user; if (**pp == 0x00) continue; if (**pp == '@') { struct group *e = getgrnam(*pp + 1); if (!e) { pr_err("Can't get group file entry for `%s'\n", *pp + 1); continue; } add_users_map(share, map, g_strdupv(e->gr_mem)); continue; } user = usm_lookup_user(*pp); if (!user) { pr_info("No user `%s' for share `%s'\n", *pp, share->name); continue; } if (g_hash_table_lookup(share->maps[map], user->name)) continue; g_hash_table_insert(share->maps[map], user->name, user); } g_free(names); } static void add_hosts_map(struct ksmbd_share *share, enum share_hosts map, char **names) { GHashTable **hosts_map; char **pp; if (map == KSMBD_SHARE_HOSTS_ALLOW_MAP) hosts_map = &share->hosts_allow_map; else if (map == KSMBD_SHARE_HOSTS_DENY_MAP) hosts_map = &share->hosts_deny_map; if (!*hosts_map) *hosts_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); for (pp = names; *pp; g_free(*pp++)) { if (**pp == 0x00) continue; /* * FIXME */ if (g_hash_table_lookup(*hosts_map, *pp)) continue; g_hash_table_insert(*hosts_map, *pp, *pp); *pp = NULL; } g_free(names); } 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 int validate_comment(struct ksmbd_share *share) { if (!g_utf8_validate(share->comment, -1, NULL)) { pr_err("Comment is not UTF-8\n"); return -EINVAL; } return 0; } static int force_group(struct ksmbd_share *share, char *name) { struct group *e = getgrnam(name); if (!e) { pr_err("Can't get group file entry for `%s'\n", name); return -EINVAL; } share->force_gid = e->gr_gid; if (share->force_gid == KSMBD_SHARE_INVALID_GID) { pr_err("Group `%s' is invalid\n", name); return -EINVAL; } return 0; } static int force_user(struct ksmbd_share *share, char *name) { struct passwd *e = getpwnam(name); if (!e) { pr_err("Can't get password file entry for `%s'\n", name); return -EINVAL; } share->force_uid = e->pw_uid; if (share->force_uid == KSMBD_SHARE_INVALID_UID) { pr_err("User `%s' is invalid\n", name); return -EINVAL; } /* `force group' has precedence */ if (share->force_gid == KSMBD_SHARE_INVALID_GID) { share->force_gid = e->pw_gid; if (share->force_gid == KSMBD_SHARE_INVALID_GID) { pr_err("Group `%s' is invalid\n", name); return -EINVAL; } } return 0; } static void process_share_conf_kv(struct ksmbd_share *share, char *k, char *v) { if (shm_share_config(k, KSMBD_SHARE_CONF_COMMENT)) { share->comment = cp_get_group_kv_string(v); if (validate_comment(share)) 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); 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); else clear_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_ACCOUNT)) { share->guest_account = cp_get_group_kv_string(v); if (usm_add_guest_account(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)) { if (force_group(share, v)) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_USER)) { if (force_user(share, v)) set_share_flag(share, KSMBD_SHARE_FLAG_INVALID); 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); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_VALID_USERS)) { add_users_map(share, KSMBD_SHARE_VALID_USERS_MAP, cp_get_group_kv_list(v)); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_INVALID_USERS)) { add_users_map(share, KSMBD_SHARE_INVALID_USERS_MAP, cp_get_group_kv_list(v)); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_READ_LIST)) { add_users_map(share, KSMBD_SHARE_READ_LIST_MAP, cp_get_group_kv_list(v)); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_LIST)) { add_users_map(share, KSMBD_SHARE_WRITE_LIST_MAP, cp_get_group_kv_list(v)); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_ADMIN_USERS)) { add_users_map(share, KSMBD_SHARE_ADMIN_USERS_MAP, cp_get_group_kv_list(v)); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_ALLOW)) { add_hosts_map(share, KSMBD_SHARE_HOSTS_ALLOW_MAP, cp_get_group_kv_list(v)); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_DENY)) { add_hosts_map(share, KSMBD_SHARE_HOSTS_DENY_MAP, cp_get_group_kv_list(v)); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_MAX_CONNECTIONS)) { share->max_connections = cp_memparse(v); if (share->max_connections <= 0 || share->max_connections > KSMBD_CONF_MAX_CONNECTIONS) share->max_connections = KSMBD_CONF_MAX_CONNECTIONS; return; } if (shm_share_config(k, KSMBD_SHARE_CONF_VETO_FILES)) { share->veto_list = cp_get_group_kv_string(v + 1); 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); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_FOLLOW_SYMLINKS)) { if (cp_get_group_kv_bool(v)) set_share_flag( share, KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS); else clear_share_flag( share, KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_VFS_OBJECTS)) { char **objects = cp_get_group_kv_list(v), **pp = objects; clear_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR); clear_share_flag(share, KSMBD_SHARE_FLAG_STREAMS); for (; *pp; pp++) { char *p = *pp; 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); return; } if (shm_share_config(k, KSMBD_SHARE_CONF_CROSSMNT)) { if (cp_get_group_kv_bool(v)) set_share_flag(share, KSMBD_SHARE_FLAG_CROSSMNT); else clear_share_flag(share, KSMBD_SHARE_FLAG_CROSSMNT); return; } } static void init_share_from_group(struct ksmbd_share *share, struct smbconf_group *group) { char *k, *v; GHashTableIter iter; share->name = g_strdup(group->name); if (group == parser.ipc) set_share_flag(share, KSMBD_SHARE_FLAG_PIPE); ghash_for_each(k, v, group->kv, iter) process_share_conf_kv(share, k, v); } 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 new share `%s' [0x%" PRIXPTR "]\n", share->name, (uintptr_t)share); 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_debug("Clashed new share `%s' [0x%" PRIXPTR "]\n", share->name, (uintptr_t)share); kill_ksmbd_share(share); return 0; } pr_debug("New share `%s' [0x%" PRIXPTR "]\n", share->name, (uintptr_t)share); 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 = NULL; 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 shm_iter_shares(share_cb cb, void *data) { struct ksmbd_share *share; GHashTableIter iter; g_rw_lock_reader_lock(&shares_table_lock); ghash_for_each(share, shares_table, iter) cb(share, 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->share_name = 0x00; strncat(resp->share_name, share->name, KSMBD_REQ_MAX_SHARE_NAME - 1); 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.5.2/tools/management/spnego.c000066400000000000000000000177341460410342700207070ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 LG Electronics * * linux-cifsd-devel@lists.sourceforge.net */ #include "tools.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]; } void spnego_init(void) { struct spnego_mech_ctx *mech_ctx; int i; mech_ctx = &mech_ctxs[SPNEGO_MECH_MSKRB5]; if (!mech_ctx->ops) mech_ctx->ops = &spnego_mskrb5_operations; if (!mech_ctx->params.krb5.service_name) mech_ctx->params.krb5.service_name = g_strdup(global_conf.krb5_service_name); if (!mech_ctx->params.krb5.keytab_name) mech_ctx->params.krb5.keytab_name = g_strdup(global_conf.krb5_keytab_file); mech_ctx = &mech_ctxs[SPNEGO_MECH_KRB5]; if (!mech_ctx->ops) mech_ctx->ops = &spnego_krb5_operations; if (!mech_ctx->params.krb5.service_name) mech_ctx->params.krb5.service_name = g_strdup(global_conf.krb5_service_name); if (!mech_ctx->params.krb5.keytab_name) mech_ctx->params.krb5.keytab_name = g_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])) abort(); } void spnego_destroy(void) { int i; for (i = 0; i < SPNEGO_MAX_MECHS; i++) { if (mech_ctxs[i].ops && 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; } 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"); g_free(oid); return -EINVAL; } g_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 5 OIDs\n"); return -EINVAL; } if (!is_supported_mech(oid, len, mech_type)) { g_free(oid); pr_debug("Not a supported mechanism\n"); return -EINVAL; } g_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 5 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 a Kerberos 5 OID\n"); g_free(oid); return -EINVAL; } g_free(oid); /* AP_REQ id */ if (asn1_read(&ctx, &id, 2) == 0 || id[0] != 1 || id[1] != 0) { g_free(id); pr_debug("Error decoding AP_REQ ID\n"); return -EINVAL; } g_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)) { g_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 = g_try_malloc0(*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); g_free(sup_oid); g_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("No support for Kerberos 5\n"); goto out; } if (mech_ctx->ops->handle_authen(mech_ctx, mech_token, token_len, auth_out, encode_negTokenTarg)) { retval = -EPERM; pr_info("Error authenticating\n"); goto out; } out: return retval; } ksmbd-tools-3.5.2/tools/management/spnego_krb5.c000066400000000000000000000246631460410342700216310ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 LG Electronics * * linux-cifsd-devel@lists.sourceforge.net */ #include "tools.h" #include #include #include #include #include #include #include #include #include #include "spnego_mech.h" #ifndef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY krb5_error_code krb5_auth_con_getrecvsubkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock **keyblock) { return krb5_auth_con_getremotesubkey(context, auth_context, keyblock); } #endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */ #ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE #define KRB5_KEY_TYPE(k) ((k)->keytype) #define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length) #define KRB5_KEY_DATA(k) ((k)->keyvalue.data) #else #define KRB5_KEY_TYPE(k) ((k)->enctype) #define KRB5_KEY_LENGTH(k) ((k)->length) #define KRB5_KEY_DATA(k) ((k)->contents) #endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */ 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 g_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 = g_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 = g_strdup(name); *host_name = get_host_name(); goto out; } *service_name = g_strndup(name, delim - name); name = delim + 1; delim = strchr(name, '@'); if (!delim) { *host_name = g_strdup(name); goto out; } *host_name = g_strndup(name, delim - name); out: /* we assume the host name is FQDN if it has "." */ if (strchr(*host_name, '.')) return 0; g_free(*service_name); g_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 credentials for %s\n", name); krb5_free_unparsed_name(context, name); goto out_err; } g_free(host_name); g_free(service_name); return 0; out_err: if (sprinc) krb5_free_principal(context, sprinc); g_free(service_name); g_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; #ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER krb5_authenticator *authenti; #else krb5_authenticator authenti; #endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */ krb5_principal client; 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 initializing 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_getrecvsubkey(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; } #ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT krb_retval = krb5_build_principal_ext(krb5_ctx->context, &client, strlen(authenti->crealm), authenti->crealm, 0); if (krb_retval) { pr_krb5_err(krb5_ctx->context, krb_retval, "while getting authenticator client\n"); goto out_free_auth; } krb_retval = copy_PrincipalName(&authenti->cname, &client->name); if (krb_retval) { pr_krb5_err(krb5_ctx->context, krb_retval, "while copying authenticator client name\n"); goto out_free_client; } #else client = authenti->client; #endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */ krb_retval = krb5_unparse_name_flags(krb5_ctx->context, 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_client; } memset(auth_out, 0, sizeof(*auth_out)); auth_out->user_name = g_try_malloc(strlen(client_name) + 1); if (!auth_out->user_name) { krb5_free_unparsed_name(krb5_ctx->context, client_name); retval = -ENOMEM; goto out_free_client; } strcpy(auth_out->user_name, client_name); krb5_free_unparsed_name(krb5_ctx->context, client_name); auth_out->sess_key = g_try_malloc(KRB5_KEY_LENGTH(session_key)); if (!auth_out->sess_key) { g_free(auth_out->user_name); retval = -ENOMEM; goto out_free_client; } memcpy(auth_out->sess_key, KRB5_KEY_DATA(session_key), KRB5_KEY_LENGTH(session_key)); auth_out->key_len = KRB5_KEY_LENGTH(session_key); if (spnego_encode(ap_rep.data, ap_rep.length, mech_ctx->oid, mech_ctx->oid_len, &auth_out->spnego_blob, &auth_out->blob_len)) { g_free(auth_out->user_name); g_free(auth_out->sess_key); goto out_free_client; } pr_info("Authenticated user `%s'\n", auth_out->user_name); retval = 0; out_free_client: #ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT krb5_free_principal(krb5_ctx->context, client); #endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */ out_free_auth: #ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER krb5_free_authenticator(krb5_ctx->context, authenti); #else krb5_free_authenticator(krb5_ctx->context, &authenti); #endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */ 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; if (mech_ctx->private) return 0; krb5_ctx = g_try_malloc0(sizeof(*krb5_ctx)); if (!krb5_ctx) return -ENOMEM; krb_retval = krb5_init_context(&krb5_ctx->context); if (krb_retval) { g_free(krb5_ctx); pr_err("while initializing krb5 context\n"); 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); g_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); g_free(krb5_ctx); mech_ctx->private = NULL; } g_free(mech_ctx->params.krb5.service_name); mech_ctx->params.krb5.service_name = NULL; g_free(mech_ctx->params.krb5.keytab_name); mech_ctx->params.krb5.keytab_name = NULL; } 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.5.2/tools/management/spnego_mech.h000066400000000000000000000020701460410342700216730ustar00rootroot00000000000000/* 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.5.2/tools/management/tree_conn.c000066400000000000000000000137051460410342700213620ustar00rootroot00000000000000// 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 "tools.h" static struct ksmbd_tree_conn *new_ksmbd_tree_conn(void) { struct ksmbd_tree_conn *conn; conn = g_try_malloc0(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); g_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_TREE_CONN_FLAG_READ_ONLY); if (test_share_flag(share, KSMBD_SHARE_FLAG_UPDATE)) set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_UPDATE); if (shm_open_connection(share)) { resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS; pr_debug("treecon: Too many connections to 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, guest not allowed\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 `%s' not found\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 users 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 valid users 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("treecon: Unable to bind tree connection\n"); g_rw_lock_writer_lock(&share->update_lock); clear_share_flag(share, KSMBD_SHARE_FLAG_UPDATE); g_rw_lock_writer_unlock(&share->update_lock); 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.5.2/tools/management/user.c000066400000000000000000000201041460410342700203530ustar00rootroot00000000000000// 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 "linux/ksmbd_server.h" #include "management/share.h" #include "management/user.h" #include "config_parser.h" #include "tools.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' [0x%" PRIXPTR "]\n", user->name, (uintptr_t)user); g_free(user->name); g_free(user->pass_b64); g_free(user->pass); g_rw_lock_clear(&user->update_lock); g_free(user); } 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); } void usm_remove_all_users(void) { struct ksmbd_user *user; GHashTableIter iter; g_rw_lock_writer_lock(&users_table_lock); ghash_for_each_remove(user, users_table, iter) { user->state = KSMBD_USER_STATE_FREEING; put_ksmbd_user(user); } 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 *e; size_t pass_sz; user = g_try_malloc0(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; e = getpwnam(name); if (!e) { user->uid = KSMBD_SHARE_INVALID_UID; user->gid = KSMBD_SHARE_INVALID_GID; } else { user->uid = e->pw_uid; user->gid = e->pw_gid; } user->pass = base64_decode(user->pass_b64, &pass_sz); user->pass_sz = (int)pass_sz; return user; } static void usm_clear_users(void) { struct ksmbd_user *user; GHashTableIter iter; ghash_for_each(user, users_table, iter) kill_ksmbd_user(user); } void usm_destroy(void) { if (users_table) { usm_clear_users(); g_hash_table_destroy(users_table); users_table = NULL; } } void usm_init(void) { if (!users_table) users_table = g_hash_table_new(g_str_hash, g_str_equal); } 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_user_name(char *name, char *p) { int is_name; is_name = p > name; if (!is_name) { pr_debug("User name is missing\n"); goto out; } is_name = p - name < KSMBD_REQ_MAX_ACCOUNT_NAME_SZ; if (!is_name) { pr_debug("User name exceeds %d bytes\n", KSMBD_REQ_MAX_ACCOUNT_NAME_SZ - 1); goto out; } is_name = g_utf8_validate(name, p - name, NULL); if (!is_name) { pr_debug("User name is not UTF-8\n"); goto out; } for (; name < p; name++) { is_name = cp_printable(name) && *name != ':'; if (!is_name) { pr_debug("User name contains `%c' [0x%.2X]\n", *name, (unsigned char)*name); goto out; } } out: return is_name; } int usm_add_new_user(char *name, char *pwd) { int ret = 0; struct ksmbd_user *user = new_ksmbd_user(name, pwd); if (!user) { g_free(name); g_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_debug("Clashed new user `%s' [0x%" PRIXPTR "]\n", name, (uintptr_t)user); kill_ksmbd_user(user); return 0; } pr_debug("New user `%s' [0x%" PRIXPTR "]\n", user->name, (uintptr_t)user); 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_guest_account(char *name) { int ret; struct ksmbd_user *user; ret = usm_add_new_user(g_strdup(name), g_strdup("NULL")); if (ret) return ret; user = usm_lookup_user(name); if (!user) { ret = -EINVAL; } else { set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT); put_ksmbd_user(user); } return ret; } void usm_iter_users(user_cb cb, void *data) { struct ksmbd_user *user; GHashTableIter iter; g_rw_lock_reader_lock(&users_table_lock); ghash_for_each(user, users_table, iter) cb(user, data); g_rw_lock_reader_unlock(&users_table_lock); } void usm_update_user_password(struct ksmbd_user *user, char *pwd) { size_t pass_sz; char *pass_b64 = g_strdup(pwd); char *pass = base64_decode(pass_b64, &pass_sz); pr_debug("Update user password: %s\n", user->name); g_rw_lock_writer_lock(&user->update_lock); g_free(user->pass_b64); g_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); } 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; } put_ksmbd_user(user); return 0; } ksmbd-tools-3.5.2/tools/meson.build000066400000000000000000000013601460410342700172620ustar00rootroot00000000000000ksmbd_tools_files = [ 'management/tree_conn.c', 'management/user.c', 'management/share.c', 'management/session.c', 'config_parser.c', 'tools.c', ] if krb5_dep.found() ksmbd_tools_files += [ 'management/spnego.c', 'asn1.c', 'management/spnego_krb5.c', ] endif executable( 'ksmbd.tools', ksmbd_tools_files, include_directories: include_dirs, c_args: [ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')), '-DRUNSTATEDIR="@0@"'.format(runstatedir), ], dependencies: [ glib_dep, krb5_dep, asn1_lib, pthread_lib, ], link_with: [ addshare_lib, adduser_lib, control_lib, mountd_lib, ], install: true, install_dir: get_option('libexecdir'), ) ksmbd-tools-3.5.2/tools/tools.c000066400000000000000000000155761460410342700164420ustar00rootroot00000000000000// 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 "tools.h" #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" int log_level = PR_INFO; int ksmbd_health_status; tool_main_fn *tool_main; 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; } G_GNUC_PRINTF(2, 0) 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); } G_GNUC_PRINTF(2, 0) 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 __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; } } int set_log_level(int level) { int old_level; if (log_level == PR_DEBUG) return log_level; old_level = log_level; log_level = level; return old_level; } #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; } return NULL; } return converted; } char **gptrarray_to_strv(GPtrArray *gptrarray) { if (!gptrarray->len || g_ptr_array_index(gptrarray, gptrarray->len - 1)) g_ptr_array_add(gptrarray, NULL); return (char **)g_ptr_array_free(gptrarray, 0); } static char *strv_to_str(char **strv) { char *str = g_strjoinv(NULL, strv); g_strfreev(strv); return str; } char *gptrarray_to_str(GPtrArray *gptrarray) { return strv_to_str(gptrarray_to_strv(gptrarray)); } void gptrarray_printf(GPtrArray *gptrarray, const char *fmt, ...) { va_list args; va_start(args, fmt); g_ptr_array_add(gptrarray, g_strdup_vprintf(fmt, args)); va_end(args); } int set_conf_contents(const char *conf, const char *contents) { GError *error = NULL; mode_t mask = umask(~(S_IRUSR | S_IWUSR | S_IRGRP)); g_file_set_contents(conf, contents, -1, &error); umask(mask); if (error) { pr_err("%s\n", error->message); g_error_free(error); return -EINVAL; } pr_info("Wrote `%s'\n", conf); return 0; } int load_config(char *pwddb, char *smbconf) { int ret; usm_init(); if (TOOL_IS_MOUNTD) usm_remove_all_users(); ret = cp_parse_pwddb(pwddb); if (ret) return ret; if (TOOL_IS_ADDSHARE) cp_smbconf_parser_init(); shm_init(); if (TOOL_IS_MOUNTD) shm_remove_all_shares(); ret = cp_parse_smbconf(smbconf); if (ret) return ret; if (TOOL_IS_MOUNTD) { sm_init(); wp_init(); rpc_init(); ipc_init(); spnego_init(); } return ret; } void remove_config(void) { if (TOOL_IS_MOUNTD) { spnego_destroy(); ipc_destroy(); rpc_destroy(); wp_destroy(); sm_destroy(); } else if (TOOL_IS_ADDSHARE) { cp_smbconf_parser_destroy(); } shm_destroy(); usm_destroy(); } int set_tool_main(char *name) { if (!strcmp(name, "ksmbd.addshare")) tool_main = addshare_main; else if (!strcmp(name, "ksmbd.adduser")) tool_main = adduser_main; else if (!strcmp(name, "ksmbd.control")) tool_main = control_main; else if (!strcmp(name, "ksmbd.mountd")) tool_main = mountd_main; else tool_main = NULL; return !tool_main ? -EINVAL : 0; } const char *get_tool_name(void) { if (TOOL_IS_ADDSHARE) return "ksmbd.addshare"; if (TOOL_IS_ADDUSER) return "ksmbd.adduser"; if (TOOL_IS_CONTROL) return "ksmbd.control"; if (TOOL_IS_MOUNTD) { if (getppid() == global_conf.pid) return "ksmbd.mountd(worker)"; if (getpid() == global_conf.pid) return "ksmbd.mountd(manager)"; return "ksmbd.mountd"; } return "ksmbd.tools"; } int show_version(void) { pr_info("ksmbd-tools version : %s\n", KSMBD_TOOLS_VERSION); return 0; } int main(int argc, char **argv) { char *base_name; if (!*argv) return EXIT_FAILURE; base_name = strrchr(*argv, '/'); base_name = base_name ? base_name + 1 : *argv; if (set_tool_main(base_name)) { pr_err("Invalid base name `%s'\n", base_name); return EXIT_FAILURE; } return tool_main(argc, argv); }