pax_global_header00006660000000000000000000000064136535460500014521gustar00rootroot0000000000000052 comment=942ec59c61ffe81a2a80e93eaa8e2a3d86565042 cado-0.9.5/000077500000000000000000000000001365354605000124425ustar00rootroot00000000000000cado-0.9.5/CMakeLists.txt000066400000000000000000000020771365354605000152100ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) project(cado VERSION 0.9.5 DESCRIPTION "Capability Ambient DO. Provide users just the capabilities they need." HOMEPAGE_URL "https://github.com/rd235/cado" LANGUAGES C) include(GNUInstallDirs) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2 -O2 -Wall -pedantic") set(LIBS_REQUIRED cap execs mhash pam pam_misc) option(CADO_RUN_POST_INSTALL "Run PostInstall.cmake" ON) foreach(THISLIB IN LISTS LIBS_REQUIRED) find_library(${THISLIB}_library ${THISLIB}) if(NOT ${THISLIB}_library) message(FATAL_ERROR "library lib${THISLIB} not found") endif() endforeach(THISLIB) if (WITHEDITOR) set(EDITOR "${WITHEDITOR}") else (WITHEDITOR) set(EDITOR "/usr/bin/vi") endif (WITHEDITOR) # Spool dir for scado files set(CADO_SPOOL_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/spool/${CMAKE_PROJECT_NAME}") configure_file( "include/config.h.in" "include/config.h" ) add_subdirectory(man) add_subdirectory(src) add_custom_target(uninstall "${CMAKE_COMMAND}" -DCADO_SPOOL_DIR=${CADO_SPOOL_DIR} -P "${PROJECT_SOURCE_DIR}/Uninstall.cmake" ) cado-0.9.5/COPYING000066400000000000000000000431231365354605000135000ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, 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 Library 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 Appendix: 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) 19yy 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 St, 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) 19yy 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 Library General Public License instead of this License. cado-0.9.5/PostInstall.cmake000066400000000000000000000022701365354605000157210ustar00rootroot00000000000000execute_process(COMMAND bash -euc "\ if ! getent group _cado >/dev/null 2>&1; then groupadd \ --system \ _cado; fi" ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND bash -euc "\ # get nologin path PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin NOLOGIN=\"\$(which nologin)\" if ! getent passwd _cado >/dev/null 2>&1; then useradd \ --no-create-home \ --home-dir /nonexistent \ --system \ --shell \"\$NOLOGIN\" \ -g _cado \ _cado; fi" ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND mkdir -p ${CADO_SPOOL_DIR} ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND chown root:_cado ${CADO_SPOOL_DIR} ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND chmod 4770 ${CADO_SPOOL_DIR} ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND chown :_cado ${BINDIR}/scado ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND chmod g+s ${BINDIR}/scado ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND chown _cado: ${BINDIR}/cado ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND chmod u+s ${BINDIR}/cado ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND ldconfig ${LIBDIR} ERROR_QUIET OUTPUT_QUIET) execute_process(COMMAND ${BINDIR}/cado --setcap ERROR_QUIET OUTPUT_QUIET) cado-0.9.5/README.md000066400000000000000000000144141365354605000137250ustar00rootroot00000000000000# cado CADO: Capability DO (like a sudo providing users with just the capabilities they need) Cado permits to delegate capabilities to users. Cado is a capability based sudo. Sudo allows authorized users to run programs as root (or as another user), cado allows authorized users to run programs with specific (ambient) capabilities. Cado is more selective than sudo, users can be authorized to have only specific capabilities (and not others). ## Install get the source code, from the root of the source tree run: ``` $ mkdir build $ cd build $ cmake .. $ make $ sudo make install ``` It installs two programs in /usr/local/bin: cado and caprint. If you want to install the programs in /usr/bin run "cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr" instead of "cmake ..". ## Configuration Cado needs a configuration file: /etc/cado.conf with the following syntax: - lines beginning with # are comments - all the other lines have two fields separated by :, the first field is a capability or a list of capabilities, the second field is a list of users or groups (group names have @ as a prefix). Capabilities can be written with or without the cap_ prefix (net_admin means cap_net_admin). Example of /etc/cado.conf file: ``` # Capability Ambient DO configuration file # cado.conf net_admin: @netadmin,renzo cap_kill: renzo ``` The file above allows the user renzo and all the members of the group named netadmin to run programs neeeding the cap_net_admin capability. The user renzo can also run programs requiring cap_kill. The file /etc/cado.conf can be owned by root and have no rw permission for users. It is also possible to use lists of capabilities: ``` setgid,setuid: giovanni ``` or exadecimal masks: ``` c0: giovanni,@idgroup ``` ## IMPORTANT Cado has been designed to work using the minimum set of capability required for its services. (following the principle of least privilege). ``` $ ls -l /etc/cado.conf -rw------- 1 root root 100 Jun 19 17:11 /etc/cado.conf ``` Cado itself is not a seuid executable, it uses the capability mechanism and it has an options to set its own capabilities. So after each change in the /etc/cado.conf, the capability set should be recomputed using the following command: ``` $ sudo cado -s ``` or ``` $ sudo cado -sv ``` (this latter command is verbose and shows the set of capabilties assigned to the cado executable file). using the example configuration file above, cado would be assigned the following capabilities: ``` $ sudo cado -sv Capability needed by cado: 2 0000000000000004 cap_dac_read_search 5 0000000000000020 cap_kill 12 0000000000001000 cap_net_admin 0000000000001024 $ /sbin/getcap /usr/local/bin/cado /usr/local/bin/cado = cap_dac_read_search,cap_kill,cap_net_admin+p ``` ## How to use The syntax of cado is simple: ``` $ cado [options] set_of_capabilities command [args] ``` for example if the user renzo wants to run a shell having the cap_net_admin capability enabled he can type the following command: ``` $ cado net_admin bash Password: $ ``` the user will be requested to authenticate himself. If the user has the right to enable cap_net_admin (from the cado.conf configuration file) and he typed in the correct password, cado starts a new shell with the requested capability enabled. It is possible define the set_of_capabilities using a list of capabilities (with or without the cap_prefix) or exadecimal masks. In the new shell the user can do all the operations permitted by the enabled capabilities, in this case, for example, he will be allowed to change the networking configuration, add tuntap interfaces and so on. It is possible to show the ambient capability set of a program by reading the /proc/####/status file: e.g.: ``` $ grep CapAmb /proc/$$/status CapAmb: 0000000000001000 ``` (cap_net_admin is the capability #12, the mask is 0x1000, i.e. 1ULL << 12) ## caprint caprint is a simple program which shows the ambient capabilities of a running program. (a pid of a running process can be specified as an optional parameter, otherwise it shows the capabilities of caprint itself) ``` $ caprint cap_net_admin $ caprint -l 12 0000000000001000 cap_net_admin ``` There is an option -p that has been designed to add the current set of ambient capabilities to the shell prompt, so it is easier for the user to recognize when a shell has some "extra power", so to avoid errors. In .bashrc or .bash_profile (or in their system-side counterparts in /etc) it is possible to set rules like the followings: ``` if which caprint >&/dev/null ; then ambient=$(caprint -p) fi PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$$ambient ' ``` The prompt becomes something like: ``` renzo@host:~$net_admin# ``` --- Some secondary features: The -v feature shows the set of available capabilities: ``` $ cado -v Allowed ambient capabilities: 5 0000000000000020 cap_kill 12 0000000000001000 cap_net_admin 0000000000001020 $ cado -v net_admin,kill bash Allowed ambient capabilities: 5 0000000000000020 cap_kill 12 0000000000001000 cap_net_admin 0000000000001020 Requested ambient capabilities: 5 0000000000000020 cap_kill 12 0000000000001000 cap_net_admin 0000000000001020 Password: ``` It is useful to show which capability/ies cannot be granted: ``` $ cado net_admin,kill,setuid bash cado: Permission denied $ cado -v net_admin,kill,setuid bash Allowed ambient capabilities: 5 0000000000000020 cap_kill 12 0000000000001000 cap_net_admin 0000000000001020 Requested ambient capabilities: 5 0000000000000020 cap_kill 7 0000000000000080 cap_setuid 12 0000000000001000 cap_net_admin 00000000000010a0 Unavailable ambient capabilities: 7 0000000000000080 cap_setuid cado: Permission denied ``` It is possible to enable only the allowed capabilities by setting the -q option (with or without -v). Using -q cado does not fail. ``` $ cado -qv net_admin,kill,setuid bash Allowed ambient capabilities: 5 0000000000000020 cap_kill 12 0000000000001000 cap_net_admin 0000000000001020 Requested ambient capabilities: 5 0000000000000020 cap_kill 7 0000000000000080 cap_setuid 12 0000000000001000 cap_net_admin 00000000000010a0 Unavailable ambient capabilities: 7 0000000000000080 cap_setuid Password: Granted ambient capabilities: 5 0000000000000020 cap_kill 12 0000000000001000 cap_net_admin 0000000000001020 renzo@host:~/tests/cado/pre$kill,net_admin# ``` cado-0.9.5/Uninstall.cmake000066400000000000000000000021011365354605000154070ustar00rootroot00000000000000set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") if(NOT EXISTS ${MANIFEST}) message(FATAL_ERROR "Cannot find install manifest: '${MANIFEST}'") endif() file(STRINGS ${MANIFEST} files) foreach(file ${files}) if(EXISTS ${file}) message(STATUS "Removing file: '${file}'") execute_process( COMMAND rm "${file}" OUTPUT_VARIABLE remove_file ) if(${remove_file}) message(FATAL_ERROR "Failed to remove file: '${file}'.") endif() else() MESSAGE(STATUS "File '${file}' does not exist.") endif() endforeach(file) message(STATUS "Removing user: '_cado'") execute_process( COMMAND userdel _cado OUTPUT_VARIABLE remove_user ) if(${remove_user}) message(FATAL_ERROR "Failed to remove user: '_cado'.") endif() message(STATUS "Removing folder: '${CADO_SPOOL_DIR}'") execute_process( COMMAND rm -r "${CADO_SPOOL_DIR}" OUTPUT_VARIABLE remove_spool_folder ) if("${remove_spool_folder}") message(FATAL_ERROR "Failed to remove folder: '${CADO_SPOOL_DIR}'.") endif() cado-0.9.5/doc/000077500000000000000000000000001365354605000132075ustar00rootroot00000000000000cado-0.9.5/doc/cado.conf000066400000000000000000000007561365354605000147740ustar00rootroot00000000000000# Capability Ambient DO configuration file # cado.conf example # syntax list_of_capabilities: list_of_users_or_groups # lists are comma separated # items in list_of_capabilities can be capability names, short capability names (without the cap_ prefix) and exadecimal masks. # using @ as a prefix of a name in list_of_users_or_groups means that it is a groupname instead of a username. # net_admin: @netadmin,renzo # net_admin,net_bind_service,net_raw,net_broadcast: @vxvdex # cap_kill: renzo cado-0.9.5/include/000077500000000000000000000000001365354605000140655ustar00rootroot00000000000000cado-0.9.5/include/cado_const.h000066400000000000000000000025001365354605000163470ustar00rootroot00000000000000#ifndef _CADO_CONST_H #define _CADO_CONST_H #include /* Default message to print in a new scado file. */ #define DEFAULT_MESSAGE \ "# This is a scado file.\n"\ "# format: executable : capabilities_list [:]\n"\ "# If you specify :,\n"\ "# scado will automatically put the checksum of the file at the end of the line\n"\ "# (for subsequent checks).\n" /* Tmp file template */ #define TMP_TEMPLATE "cado-scado.XXXXXX" /* Tmp Directory */ #define TMPDIR "/tmp" #define BUFSIZE 4096 #define DIGEST_WARNING "***********************************************************\n"\ "* WARNING: EXECUTABLE DIGEST HAS CHANGED! *\n"\ "***********************************************************\n"\ "IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n"\ "Someone could be changed your executable, and it's trying to execute a malware!\n"\ "It is also possible that the executable is just changed (maybe a system upgrade?).\n" #define COPY_DIR CADO_EXE_DIR #define COPY_TEMPLATE "cado-exe-copy.XXXXXX" #define COMMENT_LINE 0 #define NO_CHECKSUM_LINE 1 #define CALCULATE_CHECKSUM_LINE 2 #define CHECKSUM_LINE 3 #define NOT_CONSIDERED_LINE 4 #endif /* _CADO_CONST_H */ cado-0.9.5/include/cado_paths.h000066400000000000000000000004421365354605000163430ustar00rootroot00000000000000/* This file has been automatically generated. Do not edit. */ #ifndef _CADO_PATHS_H #define _CADO_PATHS_H #include /* Spool directory path */ #define SPOOL_DIR CADO_SPOOL_DIR /* Cado temporary exe directory path */ #define CADO_EXE_DIR "/tmp" #endif /* _SCADO_PATHS_H */ cado-0.9.5/include/cado_scado_check.h000066400000000000000000000002261365354605000174520ustar00rootroot00000000000000#ifndef CADO_SCADO_CHECK_H #define CADO_SCADO_CHECK_H uint64_t cado_scado_check(const char *username, const char *exe_path, char *new_path); #endif cado-0.9.5/include/capset_from_namelist.h000066400000000000000000000002471365354605000204370ustar00rootroot00000000000000#ifndef CAP_FROM_NAMEX_H #define CAP_FROM_NAMEX_H #include #include int capset_from_namelist(char *namelist, uint64_t *capset); #endif cado-0.9.5/include/compute_digest.h000066400000000000000000000014111365354605000172460ustar00rootroot00000000000000#ifndef _COMPUTE_DIGEST_H #define _COMPUTE_DIGEST_H #include #define DIGESTTYPE MHASH_SHA256 #define DIGESTLEN (mhash_get_block_size(DIGESTTYPE)) #define DIGESTSTRLEN (2*DIGESTLEN) /* compute the hash digest. store the result (hex string) in hashstr: an array of at least DIGESTSTRLEN chars return the size of the file in case of success, -1 in case of error */ ssize_t compute_digest(const char *path, char *hashstr); /* copytemp_hash copies the file whose path is 'inpath' in a temporary file whose path is created by mkstemp using 'outtemplate' */ /* During the copy, copytemp_hash computes the hash and stores the (hex) result in hashstr */ ssize_t copytemp_digest(const char *inpath, char *outtemplate, char *hashstr); #endif /* _COMPUTE_DIGEST_H */ cado-0.9.5/include/config.h.in000066400000000000000000000002071365354605000161070ustar00rootroot00000000000000/* default editor */ #cmakedefine EDITOR "/usr/bin/vi" /* SPOOL Directory for scado */ #cmakedefine CADO_SPOOL_DIR "@CADO_SPOOL_DIR@" cado-0.9.5/include/file_utils.h000066400000000000000000000006661365354605000164050ustar00rootroot00000000000000#ifndef _FILE_UTILS_H #define _FILE_UTILS_H /* copyfile copies the file infd (from the current offset) to outfd (from the current offset) */ /* the return value is the number of bytes copied */ ssize_t copyfile(int infd, int outfd); /* copytemp copies the file whose path is 'inpath' in a temporary file whose pathname has been created by mkstemp(3) using 'outtemplate; */ ssize_t copytemp(char *inpath, char *outtemplate); #endif cado-0.9.5/include/get_scado_file.h000066400000000000000000000004471365354605000171720ustar00rootroot00000000000000#ifndef _GET_SPOOL_FILE_H #define _GET_SPOOL_FILE_H #include #include /* Get the user scado file */ static inline int get_scado_file(const char *username, char *path) { return snprintf(path, PATH_MAX, "%s/%s", SPOOL_DIR, username); } #endif /* _GET_SPOOL_FILE_H */ cado-0.9.5/include/get_user_groups.h000066400000000000000000000001321365354605000174460ustar00rootroot00000000000000#ifndef GET_USER_GROUPS_H #define GET_USER_GROUPS_H char **get_user_groups(void); #endif cado-0.9.5/include/pam_check.h000066400000000000000000000001561365354605000161520ustar00rootroot00000000000000#ifndef PAM_CHECK_H #define PAM_CHECK_H #include int pam_check(char *username); #endif cado-0.9.5/include/read_conf.h000066400000000000000000000002641365354605000161600ustar00rootroot00000000000000#ifndef READ_CONF_H #define READ_CONF_H #include uint64_t get_authorized_caps(char **user_groups, uint64_t reqset); int set_self_capability(uint64_t capset); #endif cado-0.9.5/include/scado_parse.h000066400000000000000000000011331365354605000165170ustar00rootroot00000000000000#ifndef _SCADO_PARSE_H #define _SCADO_PARSE_H #include /* copy file inpath to file outpath. if path == NULL, add missing HASH digests else if *path == 0, update all HASH digests else update only the digest for path */ void scado_copy_update(char *inpath, char *outpath, char *path); /* get info for file path. if scado_path_getinfo returns 1 the path is authorized by scado, pcapset and digest are the permitted set of capabilities and the digest respectively */ int scado_path_getinfo(char *inpath, const char *path, uint64_t *pcapset, char *digest); #endif // _SCADO_PARSE.H cado-0.9.5/include/set_ambient_cap.h000066400000000000000000000004201365354605000173470ustar00rootroot00000000000000#ifndef SET_AMBIENT_CAP_H #define SET_AMBIENT_CAP_H #include void set_ambient_cap(uint64_t capset); void drop_ambient_cap(uint64_t capset); int drop_all_ambient_cap(void); void raise_cap_dac_read_search(void); void lower_cap_dac_read_search(void); #endif cado-0.9.5/man/000077500000000000000000000000001365354605000132155ustar00rootroot00000000000000cado-0.9.5/man/CMakeLists.txt000066400000000000000000000020071365354605000157540ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) set(RONN_ORGANIZATION "VirtualSquare") set(RONN_ARGS --organization=${RONN_ORGANIZATION}) # #ronn pages file(GLOB VU_RONN_PAGES ${CMAKE_CURRENT_SOURCE_DIR}/*.[1-8].ronn) set(VU_MAN_FILES) foreach(VU_RONN_PATH IN LISTS VU_RONN_PAGES) # VU_RONNPAGE: basename of VU_RONN_PATH get_filename_component(VU_RONNPAGE ${VU_RONN_PATH} NAME) # VU_MANPAGE: VU_RONNPAGE without the suffix string(REGEX REPLACE "\.ronn$" "" VU_MANPAGE ${VU_RONNPAGE}) list(APPEND VU_MAN_FILES ${VU_MANPAGE}) endforeach(VU_RONN_PATH) add_custom_target(${PROJECT_NAME}_manpages ALL make RONN_ARGS="${RONN_ARGS}" ${VU_MAN_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) ### man pages file(GLOB VU_MAN_PAGES ${CMAKE_CURRENT_SOURCE_DIR}/*.[1-8]) foreach(VU_MAN_PATH IN LISTS VU_MAN_PAGES) get_filename_component(VU_MANPAGE ${VU_MAN_PATH} NAME) string(REGEX REPLACE ".*\\." "" MAN_CHAPTER ${VU_MANPAGE}) install(FILES ${VU_MAN_PATH} DESTINATION ${CMAKE_INSTALL_MANDIR}/man${MAN_CHAPTER}) endforeach(VU_MAN_PATH) cado-0.9.5/man/Makefile000066400000000000000000000005551365354605000146620ustar00rootroot00000000000000RONN=ronn RONNOK := $(shell command -v ${RONN} 2> /dev/null) none: % : %.ronn ifdef RONNOK # copy copyright notice grep "^\.\\\\\"" $< > $@ || true # run ronn $(RONN) -r ${RONN_ARGS} --pipe $< >> $@ # delete useless trailing "" in .TH sed -i '/^\.TH /s/ ""$$//' $@ else echo "${RONN} is not available. Manpage $@ cannot be updated" >/dev/stderr >&2 endif cado-0.9.5/man/cado.1000066400000000000000000000060331365354605000142070ustar00rootroot00000000000000.TH CADO 1 "June 23, 2016" "VirtualSquare Labs" .SH NAME cado \- Capability Ambient DO .SH SYNOPSIS .B cado [ .I OPTIONS ] .I capability_list [ .I command [ .I args ] ] .SH DESCRIPTION Cado allows the system administrator to delegate capabilities to users. Cado is a capability based sudo. Sudo allows authorized users to run programs as root (or as another user), cado allows authorized users to run programs with specific (ambient) capabilities. Cado is more selective than sudo, users can be authorized to have only specific capabilities (and not others). \fIcapability_list\fR is a comma separated list of capability names or capability masks (exadecimal numbers). For brevity, the \fBcap_\fR prefix of capability names can be omitted (e.g. \fBnet_admin\fR and \fBcap_net_admin\fR have the same meaning). If it is allowed for the current user to run processes with the requested capabilities, the user is asked to type their password (or to authenticate themselves as required by pam unless \fB-S\fR or \fB--scado\fR). Once the authentication succeeds, \fBcado\fR executes the command granting the required ambient capabilities. If \fIcommand\fR is omitted cado launch the command specified in the environment variable $SHELL. The file /etc/cado.conf (see \fBcado.conf\fR(5)) defines which capabilities can be provided by \fBcado\fR to each user. Cado itself is not a setuid executable, it uses the capability mechanism and it has an option to set its own capabilities. So after each change in the /etc/cado.conf, the capability set should be recomputed by root using the command \fBcado -s\fR or \fBcado --setcap\fR. When \fBcado\fR runs is scado mode (by the option \fB-S\fR or \fB--scado\fR), if .br \ \ - the current user is allowed to run processes with the requested capabilities, .br \ \ - the \fBcommand\fR argument is an absolute pathname and .br \ \ - there is a specific authorization line in the user's scado file, .br \fBcado\fR runs the command granting the required ambient capabilities without any further authentication request (it does not prompt for a password). .br .SH OPTIONS .I cado accepts the following options: .TP \fB\-v .TQ \fB\-\-verbose run in verbose mode. \fBcado\fR shows the set of allowed capabilities, requested cababilities, unavailable capabilities and (in case of -s) the set of capabilities assigned to \fBcado.conf\fR itself. .TP \fB\-f .TQ \fB\-\-force do not fail in case the user asks for unavailable capabilities, \fBcado\fR in this case grants the intersection between the set of requested cababilities and the set of allowed capabilities .TP \fB\-s .TQ \fB\-\-setcap \fBcado\fR computes the miminal set of capability required by itself and sets the file capability of the cado executable. .TP \fB\-S .TQ \fB\-\-scado launch \fBcado\fR with \fBscado\fR(1) support. \fIcommand\fR must be an absolute pathname and a specific authorization line must appear in the user's scado file. .TP \fB\-h .TQ \fB\-\-help print a short usage banner and exit. .SH SEE ALSO \fBcado.conf\fR(5), \fBcaprint\fR(1), \fBscado\fR(1), \fBcapabilities\fR(7) cado-0.9.5/man/cado.conf.5000066400000000000000000000040541365354605000151400ustar00rootroot00000000000000.TH CADO.CONF 5 "June 23, 2016" "VirtualSquare Labs" .SH NAME cado.conf \- Capability Ambient DO: configuration file .SH DESCRIPTION The \fB/etc/cado.conf\fR file is used to configure which ambient cabalities can be provided by \fBcado\fR to users. \fBcado\fR uses the capability cap_dac_read_search to access \fB/etc/cado.conf\fR, so this configuration does not need to be readable by users. All lines beginning with the sign '#' are comments. Non-comment lines have the following syntax .nf \fIlist_of_capabilities\fB:\fI list_of_users_and_groups\fR .fi or .nf \fIlist_of_capabilities\fB:\fI list_of_users_and_groups\fB:\fR \fIlist_of_auth_commands\fR .fi Both \fIlist_of_capabilities\fR and \fIlist_of_users_and_groups\fR are comma separated lists of identifiers. Items of \fIlist_of_capabilities\fR are capability names or capability masks (exadecimal numbers). For brevity, the \fBcap_\fR prefix of capability names can be omitted (e.g. \fBnet_admin\fR and \fBcap_net_admin\fR have the same meaning). Items of \fIlist_of_users_and_groups\fR are usernames or groupnames (groupnames must be prefexed by '@'). \fIlist_of_auth_commands\fR is a command or a list of commands separated by semicolon (;). If present, cado runs all the sequence of commands it grants the capabilities as defined in the current line only if all return zero as their exit status. Example of \fBcado.conf\fR file: .nf # Capability Ambient DO configuration file # cado.conf net_admin: @netadmin,renzo: /usr/bin/logger cado net_admin $USER; /bin/echo OK net_admin: @privatenet: /usr/local/lib/cado_autorize_privatenet net_admin,net_bind_service,net_raw,net_broadcast: @vxvdex cap_kill: renzo .fi In this example the renzo's processes can be granted (by \fBcado\fR) cap_net_admin and cap_kill. cap_net_admin can be acquired by processes owned by users belonging to the netadmin group. Users in vxvdex can provide their processes with a subset of cap_net_admin, cap_net_bind_service, cap_net_raw and cap_net_broadcast .SH SEE ALSO \fBcado\fR(1), \fBcaprint\fR(1), \fBcapabilities\fR(7) cado-0.9.5/man/cadrop.1000066400000000000000000000011321365354605000145440ustar00rootroot00000000000000.TH CADROP 1 "November 26, 2016" "VirtualSquare Labs" .SH NAME cadrop \- Capability Ambient Drop .SH SYNOPSIS .B cadrop [ .I capability_list [ .I command [ .I args ] ] ] .SH DESCRIPTION Cadrop allows users to drop (ambient) capabilities. Cadrop launches the command indicated as a parameter ($SHELL if omitted) dropping all the capabilities listed in the capability_list. If the capability_list is also omitted or it matches one of the string "all" or "-", cadrop drops all the capabilities. .SH SEE ALSO \fBcado\fR(1), \fBcaprint\fR(1), \fBscado\fR(1), \fBcado.conf\fR(5), \fBcapabilities\fR(7) cado-0.9.5/man/caprint.1000066400000000000000000000025331365354605000147420ustar00rootroot00000000000000.TH CAPRINT 1 "June 23, 2016" "VirtualSquare Labs" .SH NAME caprint \- Capability Ambient Print .SH SYNOPSIS .B caprint [ OPTIONS ] [ .I pid ] .SH DESCRIPTION \fBcaprint\fR shows the ambient capabilities of a running program (whose pid can be specified as an optional parameter, otherwise \fBcaprint\fR shows the capabilities of caprint itself). .SH OPTIONS .I caprint accepts the following options: .TP \fB\-l .TQ \fB\-\-long run in verbose mode. \fBcaprint\fR shows the bitmask for each capability owned by the process and the resulting mask of the capability set. .TP \fB\-c .TQ \fB\-\-compact emit a compact output (a single line composed of a comma separated list of the capability short names). This output can be copied as a capability list for the command \fBcado\fR(1). .TP \fB\-p .TQ \fB\-\-prompt this option has been created to provide users with a suitable shell prompt to warn the users of the extra capabilities granted to that shell (and of the extra danger in case of running wrong commands). As an example, it is possible to set the \fBbash\fR prompt editing the \fB.bashrc\fR in the user's home directory: .nf if which caprint >&/dev/null ; then ambient=$(caprint -p) fi PS1='\\u@\\h:\\w\\$$ambient' .fi .TP \fB\-h .TQ \fB\-\-help print a short usage banner and exit. .SH SEE ALSO \fBcado\fR(1), \fBcapabilities\fR(7), cado-0.9.5/man/scado.1000066400000000000000000000104511365354605000143710ustar00rootroot00000000000000.TH SCADO 1 "June 23, 2016" "VirtualSquare Labs" .SH NAME scado \- Script Capability Ambient DO .SH SYNOPSIS .B scado .B -D | .B -e | .B -l .br .B scado .B -u .I command | .B -U .br .B scado .B -h .SH DESCRIPTION \fBcado(1)\fR allows the system administrator to delegate capabilities to users. Users can grant a subset of these ambient capabilities to trusted programs. Each user can define their own list of trusted programs and which capabilities to grant, using a scado file. \fBcado -S\fR or \fBcado --scado\fR run those trusted programs without any further authentication. In this way it is also possible to run programs requiring specific capabilities within a bash script. \fBScado\fR is the command a user can run to create, edit, check or delete their own scado file. Each line of a scado file file has the following syntax: .br .RS 4 .I path_of_the_executable_file : capability_list .RE .br or .br .RS 4 .I path_of_the_executable_file : capability_list : sha256_digest_of_the_executable .RE .br (See the EXAMPLES section at the end of the man page for more info. All the trailing part of a line following a # sign is a comment.). The \fIpath_of_the_executable_file\fR must be absolute. The \fIcapability_list\fR is a comma separated list of capability names or capability masks. For brevity, the \fBcap_\fR prefix of capabilities names can be omitted (e.g. \fBnet_admin\fR and \fBcap_net_admin\fR have the same meaning). The \fIsha256_digest_of_the_executable\fR prevents \fITOCTTOU\fR attacks. When a user wants to run the file at \fIpath_of_the_executable_file\fR granting it some of the capabilities in the \fIcapability_list\fR, the permission is denied if its sha256 digest does not match \fIsha256_digest_of_the_executable\fR. If there are only two colon (\fB:\fR) separated fields in a line, it means that the user trusts a priori the integrity of the file whose pathname is \fIpath_of_the_executable_file\fR. It can be, for example, a program in /bin or /usr/bin not modifiable by users. If there are three fields (i.e. two colon characters), it means that the user wants the cryptographic digest check on the executable file integrity. When a user edits their scado file, if the field (\fIsha256_digest_of_the_executable\fR) is empty, \fBscado\fR computes it automatically when the scado file is saved. \fBScado\fR asks for user authentication by PAM to confirm any modification of the scado file. There is also a \fITOCTTOU\fR protection at running time: \fDcado -S\fR copies the executable file in a safe place, where the user cannot change it, and runs it only if the integrity check on it succeeds. The user (or a malicious intruder acting as the user) cannot modify the file after the integrity check has completed and before the program is loaded. .SH OPTIONS .I scado accepts the following options: .TP \fB\-l .TQ \fB\-\-list Display the current scado file. The actual file in the file system is not accessible by unprivileged users, for security reasons. .TP \fB\-e .TQ \fB\-\-edit Edit the scado file of the current user using the editor specified by either the \fBVISUAL\fR or the \fBEDITOR\fR environment variable (checked in that order). After you exit from the editor, the modified file will be installed automatically. .TP \fB\-D .TQ \fB\-\-delete Delete the current user's scado file. .TP \fB\-u\fR \fIcommand\fR .TQ \fB\-\-update\fR \fIcommand\fR Recompute the hash of the line which starts with \fIcommand\fR. .TP \fB\-U .TQ \fB\-\-update-all Update all the digest entries. .TP \fB\-h .TQ \fB\-\-help print a short usage banner and exit. .SH EXCEPTIONS FILES EXAMPLES .PP Allow \fBcado -S\fR to run /bin/ping providing it with the cap_net_raw capability, without any integrity check: .RS 4 /bin/ping : cap_net_raw .RE .PP Allow the activation of ping with cap_net_raw provided it has a specific SHA256 digest .RS 4 /bin/ping : cap_net_raw : dcb237f1cb20ee7b1550900d1b524c554063fd17fc673c56d341736ced6bed4b .RE .PP Compute the SHA256 digest of (the current version of) ping so, allow the activation of ping with cap_net_raw provided it has not been modified. .RS 4 /bin/ping : cap_net_raw : .RE .PP If one of the example lines here above has been inserted in the user scado file using \fBscado -e\fR, it is possible to execute ping as follows: .RS 4 cado -S cap_net_raw /bin/ping .RE .SH SEE ALSO \fBcado\fR(1), \fBcapabilities\fR(7) cado-0.9.5/src/000077500000000000000000000000001365354605000132315ustar00rootroot00000000000000cado-0.9.5/src/CMakeLists.txt000066400000000000000000000027721365354605000160010ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) add_executable( cado cado.c pam_check.c get_user_groups.c capset_from_namelist.c read_conf.c set_ambient_cap.c compute_digest.c file_utils.c scado_parse.c cado_scado_check.c ) target_include_directories(cado PRIVATE ${PROJECT_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include) target_link_libraries( cado ${pam_library} ${pam_misc_library} ${cap_library} ${mhash_library} ${execs_library}) add_executable(cadrop cadrop.c capset_from_namelist.c set_ambient_cap.c) target_include_directories(cadrop PRIVATE ${PROJECT_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include) target_link_libraries(cadrop ${cap_library}) add_executable( scado scado.c pam_check.c file_utils.c compute_digest.c capset_from_namelist.c scado_parse.c ) target_include_directories(scado PRIVATE ${PROJECT_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include) target_link_libraries( scado ${pam_library} ${pam_misc_library} ${cap_library} ${mhash_library} ${execs_library} ) add_executable(caprint caprint.c) target_include_directories(caprint PRIVATE ${PROJECT_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include) target_link_libraries(caprint ${cap_library}) install( TARGETS cado cadrop scado caprint RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) if(CADO_RUN_POST_INSTALL) install( CODE "execute_process( COMMAND ${CMAKE_COMMAND} -DBINDIR=${CMAKE_INSTALL_FULL_BINDIR} -DLIBDIR=${CMAKE_INSTALL_FULL_LIBDIR} -DCADO_SPOOL_DIR=${CADO_SPOOL_DIR} -P ${PROJECT_SOURCE_DIR}/PostInstall.cmake )" ) endif() cado-0.9.5/src/cado.c000066400000000000000000000140451365354605000143070ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* print a capset (in case of -v, verbose mode). */ static void printcapset(uint64_t capset, char *indent) { if (capset) { cap_value_t cap; int count=0; for (cap = 0; cap <= CAP_LAST_CAP; cap++) { if (capset & (1ULL << cap)) { count ++; printf("%s%2d %016" PRIx64 " %s\n", indent, cap, UINT64_C(1) << cap, cap_to_name(cap)); } } if (count > 1) printf("%s %016" PRIx64 "\n", indent, capset); } else printf("%s %016" PRIx64 " NONE\n",indent, UINT64_C(0)); } /* command line args management */ #define OPTSTRING "hfvsS" struct option long_options[]={ {"help", no_argument, NULL, 'h'}, {"force", no_argument, NULL, 'f'}, {"verbose", no_argument, NULL, 'v'}, {"setcap", no_argument, NULL, 's'}, {"scado", no_argument, NULL, 'S'}, {0,0,0,0} }; void usage(char *progname) { fprintf(stderr,"%s - execute a command in a different capability ambient\n\n",progname); fprintf(stderr,"usage: %s OPTIONS capability_list [command [args]]\n\n",progname); fprintf(stderr,"Options:\n"); fprintf(stderr," -h, --help display help message and exit\n"); fprintf(stderr," -f, --force do not display warnings, do what is allowed\n"); fprintf(stderr," -v, --verbose generate extra output\n"); fprintf(stderr," -S, --scado check scado pre-authorization for scripts\n"); fprintf(stderr," -s, --setcap set the minimum caps for %s (root access)\n",progname); exit(1); } int main(int argc, char*argv[]) { char *progname=basename(argv[0]); char **user_groups=get_user_groups(); uint64_t okcaps; uint64_t reqcaps; uint64_t grantcap=0; int verbose=0; int force=0; int setcap=0; int scado=0; int pam_check_required = 1; char copy_path[PATH_MAX] = ""; char *argvsh[]={getenv("SHELL"),NULL}; char **cmdargv; while (1) { int c=getopt_long(argc, argv, OPTSTRING, long_options, NULL); if (c < 0) break; switch (c) { case 'h': usage(progname); break; case 'v': verbose=1; break; case 'f': force=1; break; case 's': setcap=1; break; case 'S': scado=1; break; } } /* setcap mode: cado sets the minimal required set of capability required by itself */ if (setcap) { if (setuid(0) != 0 || geteuid() != 0) { fprintf(stderr, "setcap requires root access %d\n",geteuid()); exit(2); } okcaps = get_authorized_caps(NULL, -1LL); okcaps |= 1ULL << CAP_DAC_READ_SEARCH; if (verbose) { printf("Capability needed by %s:\n", progname); printcapset(okcaps, " "); } if (set_self_capability(okcaps) < 0) { fprintf(stderr, "Cannot set %s capabilities\n", progname); exit(2); } exit(0); } if (user_groups == NULL) { fprintf(stderr, "No passwd entry for user '%d'\n",getuid()); exit(2); } /* -v without any other parameter: cado shows the set of ambient capabilities allowed for the current user/group */ if (verbose && (argc == optind)) { okcaps=get_authorized_caps(user_groups, -1LL); printf("Allowed ambient capabilities:\n"); printcapset(okcaps, " "); exit(0); } if (argc - optind < 1) usage(progname); /* parse the set of requested capabilities */ if (capset_from_namelist(argv[optind], &reqcaps)) { fprintf(stderr, "List of capabilities: syntax error\n"); exit(2); } if (verbose) { printf("Requested ambient capabilities:\n"); printcapset(reqcaps, " "); } /* check if the capability requested are also allowed */ okcaps=get_authorized_caps(user_groups, reqcaps); optind++; if (optind < argc) cmdargv = argv + optind; else { cmdargv = argvsh; if (cmdargv[0] == NULL) { fprintf(stderr, "Error: $SHELL env variable not set.\n"); exit(1); } } /* scado mode, check if there is a pre-authorization for the command */ if (scado) { uint64_t scado_caps = cado_scado_check(user_groups[0], cmdargv[0], copy_path); if (verbose) { printf("Scado permitted capabilities for %s:\n", cmdargv[0]); printcapset(scado_caps, " "); } okcaps &= scado_caps; pam_check_required = 0; } /* the user requested capabilities which are not allowed */ if (reqcaps & ~okcaps) { if (verbose) { printf("Unavailable ambient capabilities:\n"); printcapset(reqcaps & ~okcaps, " "); } /* if not in "force" mode, do not complaint */ if (!force) { fprintf(stderr,"%s: Permission denied\n",progname); exit(2); } } grantcap = reqcaps & okcaps; /* revert setgid mode */ if (setuid(getuid()) < 0) { fprintf(stderr,"%s: setuid failure\n",progname); exit(2); } /* ask for pam authorization (usually password) if required */ if (pam_check_required && pam_check(user_groups[0]) != PAM_SUCCESS) { fprintf(stderr,"%s: Authentication failure\n",progname); exit(2); } /* okay: grantcap can be granted, do it! */ if (grantcap) set_ambient_cap(grantcap); if (verbose && (reqcaps & ~okcaps)) { printf("Granted ambient capabilities:\n"); printcapset(grantcap, " "); } /* exec the command in the new ambient capability environment */ execvp(copy_path[0] == 0 ? cmdargv[0] : copy_path, cmdargv); exit(2); } cado-0.9.5/src/cado_scado_check.c000066400000000000000000000141701365354605000166140ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Davide Berardi and Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void print_digest_warning(void) { fprintf(stderr, "%s", DIGEST_WARNING); } /* Avoiding TOCTOU attacks: The executable file is copied in a safe place where the user cannot modify it. (cado is setuid cado). The digest of the copy is compared against the digest reported in the scado configuration file. If the digests are the same, cado runs the copy */ /* A watchdog process unlinks the copy as soon as either cado terminates or has started the command (execve system call) */ /* copy_and_check_digest returns 0 if it succeeds, i.e. - a file at path exists and it is readable - the hash digest of the file is equal to digest and in this case newpath returns the newpath of the tmpfile to run */ /* if scado does not require to check the digest, copy_and_check_digest returns an empty string in newpath, i.e *newpath == 0 */ /* if copy_and_check_digest fails, it returns -1 */ static int copy_and_check_digest(const char *path, char *digest, char *newpath) { size_t newpathlen; /* grandchild -> grandparent pipe: write the path of the tmp copy and close in case of error: close (without writing anything) in case of hash mismatch write a NULL byte and close */ int gc_pipe[2]; pid_t childpid; /* grandparent -> grandchild pipe: used to check when the exec occours. */ /* this pipe has the FD_CLOEXEC bit set. it is unvoluntarily closed either when an exec succeeds or when the grandparent terminates (end or abend, it does not matter) */ /* when the grandchild gets the EOF it unlinks the temporary file */ int gp_pipe[2]; if (!path || !digest || !newpath) { return -1; } if (snprintf(newpath, PATH_MAX, "%s/%s", COPY_DIR, COPY_TEMPLATE) < 0) { return -1; } newpathlen = strlen(newpath); if (pipe(gc_pipe)) { perror("pipe"); return -1; } if (pipe(gp_pipe)) { perror("pipe"); return -1; } if (fcntl(gp_pipe[0], F_SETFD, FD_CLOEXEC) || fcntl(gp_pipe[1], F_SETFD, FD_CLOEXEC)) { perror("fdcntl cloexec"); return -1; } /* Start garbage collector * Double fork to avoid zombie processes and to remove the temporary file when it is not needed any more */ if(!(childpid=fork())) { /* Child */ if(!fork()) { /* Grandchild */ char newdigest[DIGESTSTRLEN + 1]; char c; int exit_status = 1; /* if the grandparent terminates before reading the newpath, write returns EPIPE. So even in this situation the garbage gets collected i.e. unlink(newpath)*/ /* setsid to avoid the propagation of signals to the process group (e.g. ^C) */ if (close(gc_pipe[0]) || close(gp_pipe[1]) || setsid() < 0 || signal(SIGPIPE, SIG_IGN) == SIG_ERR) exit(-1); if (copytemp_digest(path, newpath, newdigest) < 0) { perror("copytemp_hash"); exit(-1); } //debug only //printf("GS %s\n%s\n%s\n", newpath,digest,newdigest); if (chmod(newpath, 00611) < 0) goto end; if (strcmp(digest,newdigest) == 0) { if (write(gc_pipe[1], newpath, newpathlen + 1) < 1) goto end; } else { char err = 0; if (write(gc_pipe[1], &err, 1) < 1) goto end; } if (close(gc_pipe[1])) goto end; /* No data should be written on the pipe from the other end. This is only * to check when the parent calls exec() (or terminates)*/ while (read(gp_pipe[0], &c, sizeof(char)) > 0) ; close(gp_pipe[0]); exit_status = 0; end: unlink(newpath); exit(exit_status); } else { exit(0); } } waitpid(childpid, NULL, 0); /* (grand) Parent */ if (close(gc_pipe[1]) || close(gp_pipe[0])) { perror("close pipe"); return -1; } /* it should read a string of the same length of the original newpath which is the template for the tmp file (as described in mkstemp(3) */ int n; if ((n=read(gc_pipe[0], newpath, newpathlen + 1)) <= 0) { return -1; } if (close(gc_pipe[0])) { perror("close"); } if (newpath[0] == 0) { print_digest_warning(); return -1; } return 0; } /* given the username and the command path, cado_scado_check returns the set of permitted capabilities, as defined by the scado(1) command */ uint64_t cado_scado_check(const char *username, const char *exe_path, char *new_path) { char scado_file[PATH_MAX]; char digest[DIGESTSTRLEN + 1]; int rv; uint64_t capset = 0; if (!username || !exe_path || !new_path) return 0; if (get_scado_file(username, scado_file) <= 0){ perror("get scado file"); return 0; } raise_cap_dac_read_search(); rv = scado_path_getinfo(scado_file, exe_path, &capset, digest); lower_cap_dac_read_search(); /* default value: do not run a copy, directly run the command */ *new_path = 0; if (rv <= 0) { /* error: no capabilities canbe granted */ if (rv < 0) perror("error opening scado file"); return 0; } else { /* if no digest was specified in the scado configuration line for the current command: the capabilities in capset can be granted. otherwise copy the executable file to avoid TOCTOU attacks */ if (*digest == 0 || copy_and_check_digest(exe_path, digest, new_path) == 0) return capset; else return 0; } } cado-0.9.5/src/cadrop.c000066400000000000000000000033251365354605000146500ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli, Davide Berardi, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include #include static int argv1_all(char *s) { if (s == NULL) return 1; if (strcmp(s,"") == 0) return 1; if (strcmp(s,"-") == 0) return 1; if (strcmp(s,"all") == 0) return 1; return 0; } int main(int argc, char *argv[]) { uint64_t capset = -1; char *argvsh[]={getenv("SHELL"),NULL}; switch (argc) { case 1: capset = -1; argv += 1; break; default: if (argv1_all(argv[1])) capset = -1; else if (capset_from_namelist(argv[1], &capset) < 0) { fprintf(stderr, "error parsing capabilities\n"); exit(2); } argv+=2; break; } if (*argv == NULL) { if (*argvsh == NULL) { fprintf(stderr, "Error: $SHELL env variable not set.\n"); exit(1); } argv = argvsh; } drop_ambient_cap(capset); execvp(argv[0],argv); perror("exec"); return 2; } cado-0.9.5/src/caprint.c000066400000000000000000000065301365354605000150410ustar00rootroot00000000000000/* * caprint: print the set of ambient capability of a process. * Copyright (C) 2016 Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include #include #include #include #include char *tag="CapAmb:\t"; uint64_t get_capamb(pid_t pid) { FILE *f; char filename[32]; int status=0; int target=strlen(tag); uint64_t capamb=0; int c; snprintf(filename,32,"/proc/%d/status",pid); f=fopen(filename,"r"); if (f==NULL) exit(2); while ((c = getc(f)) != EOF) { if (c == tag[status]) { status++; if (status == target) { int fields = 0; if ((fields = fscanf(f,"%" SCNx64 "",&capamb)) != 1) fprintf(stderr, "WARNING: fscanf on %s return %d fields.\n", filename, fields); break; } } else status=0; } fclose(f); return capamb; } #define OPTSTRING "hpcl" struct option long_options[]={ {"help", no_argument, NULL, 'h'}, {"prompt", no_argument, NULL, 'p'}, {"compact", no_argument, NULL, 'c'}, {"long", no_argument, NULL, 'l'}, }; void usage(char *progname) { fprintf(stderr,"%s - show the current capability ambient\n\n",progname); fprintf(stderr,"usage: %s OPTIONS [pid]\n\n",progname); fprintf(stderr,"Options:\n"); fprintf(stderr," -h, --help display help message and exit\n"); fprintf(stderr," -p, --prompt single line output for the command prompt\n"); fprintf(stderr," -c, --compact single line without cap_ prefix\n"); fprintf(stderr," -l, --long display cap# and mask\n"); exit(1); } int main(int argc, char *argv[]) { uint64_t capamb; char *progname=basename(argv[0]); int prompt=0; int compact=0; int longlist=0; while (1) { int c=getopt_long(argc, argv, OPTSTRING, long_options, NULL); if (c < 0) break; switch (c) { case 'h': usage(progname); break; case 'p': prompt=compact=1; break; case 'c': compact=1; break; case 'l': longlist=1; break; default: usage(progname); } } if (argc - optind > 1) usage(progname); if (optind < argc) capamb=get_capamb(atoi(argv[optind])); else capamb=get_capamb(getpid()); if (capamb) { cap_value_t cap; char *sep=""; int count=0; for (cap = 0; cap <= CAP_LAST_CAP; cap++) { if (capamb & (1ULL << cap)) { count++; if (longlist) printf("%2d %016llx %s\n",cap,1ULL< 1) printf(" %016" PRIx64 "\n",capamb); } return 0; } cado-0.9.5/src/capset_from_namelist.c000066400000000000000000000037661365354605000176070ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include static int addcap(char *name, uint64_t *capset) { char *tail; uint64_t exacaps=strtoull(name, &tail, 16); if (*tail == 0) { *capset |= exacaps; return 0; } else { int rv; cap_value_t thiscap; if (strncmp(name,"cap_",4) == 0) rv=cap_from_name(name, &thiscap); else { int xnamelen=strlen(name)+5; char xname[xnamelen]; snprintf(xname,xnamelen,"cap_%s",name); rv=cap_from_name(xname, &thiscap); } if (rv >= 0) *capset |= 1ULL << thiscap; return rv; } } /* convert a list of comma separated capability tags to a bitmask of capabilities */ /* capset_from_namelist allows capability names with or without the "cap_" prefix. */ int capset_from_namelist(char *namelist, uint64_t *capset) { int rv=0; char *onecap; char *tmptok; char *spacetok; size_t namelistlen = strlen(namelist) + 1; char namelist_cpy[namelistlen]; char *namelist_ptr = namelist_cpy; *capset = 0; strncpy(namelist_ptr, namelist, namelistlen); for (; (onecap = strtok_r(namelist_ptr,",",&tmptok)) != NULL; namelist_ptr = NULL) rv |= addcap(strtok_r(onecap," \t",&spacetok), capset); return rv; } cado-0.9.5/src/compute_digest.c000066400000000000000000000051701365354605000164130ustar00rootroot00000000000000/* * scado: setuid in capability sauce. * Copyright (C) 2016 Davide Berardi and Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include #include #include #define BUFSIZE 1024 /* compute the hash digest for file whose descriptor is infd. if outfd >= 0 copy the contents to the file whose descriptor is outfd. the return value is the size of the file (or a negative value in case of error). the size of ascii_digest must be at least DIGESTSTRLEN + 1*/ static ssize_t fcompute_digest(int infd, int outfd, char *ascii_digest) { char buf[BUFSIZE]; unsigned char binary_digest[DIGESTLEN]; MHASH td; ssize_t n; ssize_t rv = 0; td = mhash_init(DIGESTTYPE); while ((n=read(infd,buf,BUFSIZE)) > 0) { mhash(td, buf, n); if (outfd >= 0) { if (write(outfd, buf, n) < 0) { n = -1; break; } } rv += n; } mhash_deinit(td, binary_digest); if (n >= 0) { int i; for (i = 0; i < DIGESTLEN; i++, ascii_digest += 2) sprintf(ascii_digest, "%.2x", binary_digest[i]); *ascii_digest = 0; } return (n < 0) ? n : rv; } /* compute the hash digest of the file (the first arg is the pathname) */ ssize_t compute_digest(const char *path, char *digest) { int fd=open(path, O_RDONLY); if (fd >= 0) { ssize_t rv = fcompute_digest(fd, -1, digest); close(fd); return rv; } else return -1; } /* compute the hash digest of the file (the first arg is the pathname) while copying it in a temporary file. The second arg is a template for tmp files as explained in mkstemp(3) */ ssize_t copytemp_digest(const char *inpath, char *outtemplate, char *digest) { int infd=open(inpath, O_RDONLY); int outfd; mode_t oldmask; ssize_t rv; if (infd < 0) return -1; oldmask = umask(027); outfd = mkstemp(outtemplate); umask(oldmask); if (outfd < 0) return -1; rv = fcompute_digest(infd, outfd, digest); close(infd); close(outfd); return rv; } cado-0.9.5/src/file_utils.c000066400000000000000000000037071365354605000155430ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include #include #undef __linux__ #ifdef __linux__ #include #define MAXSENDFILE 0x7ffff000 #else #define BUFSIZE 1024 #endif /* copy a file: when possible use a fast system call */ ssize_t copyfile(int infd, int outfd) { ssize_t rv=0; ssize_t n; #ifdef __linux__ do rv += n = sendfile(outfd, infd, NULL, MAXSENDFILE); while (n == MAXSENDFILE); #else static int pagesize; if (__builtin_expect((pagesize == 0),0)) pagesize=sysconf(_SC_PAGESIZE); char buf[BUFSIZE]; while ((n = read(infd, buf, BUFSIZE)) > 0) rv += write(outfd, buf, n); #endif return (n < 0) ? n : rv; } /* create a temporary copy of a file (using copyfile) */ /* outtemplate is a template for temporary files as explained in mkstemp(3) */ ssize_t copytemp(char *inpath, char *outtemplate) { int infd=open(inpath, O_RDONLY); int outfd; mode_t oldmask; ssize_t rv; if (infd < 0) return -1; oldmask = umask(077); outfd = mkstemp(outtemplate); umask(oldmask); if (outfd < 0) { close(infd); return -1; } rv = copyfile(infd, outfd); close(infd); close(outfd); return rv; } cado-0.9.5/src/get_user_groups.c000066400000000000000000000037501365354605000166160ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include #include #include /* if the user does not exist get_user_groups returns NULL, otherwise it returns a dynamic allocated array whose first element (0) is the username of the current user. The following elements are the names of all the groups the current user belongs to. A NULL element tags the end of the array */ char **get_user_groups(void) { uid_t uid=getuid(); struct passwd *pwd=getpwuid(uid); int ngroups=0; char **user_groups=NULL; if (pwd == NULL) return NULL; getgrouplist(pwd->pw_name, pwd->pw_gid, NULL, &ngroups); if (ngroups > 0) { gid_t gids[ngroups]; struct group *grp; user_groups = calloc(ngroups+2, sizeof(char *)); if (user_groups) { int i=0; user_groups[i++] = strdup(pwd->pw_name); if (getgrouplist(pwd->pw_name, pwd->pw_gid, gids, &ngroups) == ngroups) { while ((grp=getgrent()) != NULL) { int j; for (j=0; jgr_gid == gids[j]) { gids[j] = -1; user_groups[i++] = strdup(grp->gr_name); } } } user_groups[i] = NULL; endgrent(); } } } return user_groups; } cado-0.9.5/src/pam_check.c000066400000000000000000000025121365354605000153070ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #define _GNU_SOURCE #include #include #include #include #include #include /* call PAM to authorize the current user. usually this means to prompt the user for a password, but it can be configured using PAM */ int pam_check(char *username) { pam_handle_t* pamh; struct pam_conv pamc={.conv=&misc_conv, .appdata_ptr=NULL}; int rv; pam_start ("cado", username, &pamc, &pamh); rv= pam_authenticate (pamh, 0); pam_end (pamh, 0); return rv; } cado-0.9.5/src/read_conf.c000066400000000000000000000103051365354605000153140ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #ifndef CONFDIR #define CONFDIR "/etc" #endif #define CADO_CONF CONFDIR "/cado.conf" /* cado.conf management */ /* groupmatch returns 1 if group belongs to grouplist */ static int groupmatch (char *group, char **grouplist) { for (;*grouplist; grouplist++) { //printf("%s %s\n",group, *grouplist); if (strcmp(group, *grouplist) == 0) return 1; } return 0; } /* execs security, children must drop their capabilities */ static int drop_capabilities(void *useless) { return drop_all_ambient_cap(); } /* get_authorized_caps returns the set of authorized capabilities for the user user_groups[0] belonging to the groups user_groups[1:] */ /* if user_groups==NULL, get_authorized_caps computes the maximum set of capabilities that cado itself must own to be able to assign */ uint64_t get_authorized_caps(char **user_groups, uint64_t reqset) { uint64_t ok_caps=0; FILE *f; /* allow environment vars in execs commands */ s2argv_getvar=getenv; /* cado.conf is not readble by users. Add the capability to do it */ if (user_groups) raise_cap_dac_read_search(); f=fopen(CADO_CONF, "r"); if (f) { char *line=NULL; size_t n=0; /* set execs security, children must drop their capabilities */ execs_fork_security=drop_capabilities; while (getline(&line, &n, f) > 0 && (reqset & ~ok_caps)) { //printf("%s",line); char *scan=line; char *tokencap; char *tokenusergroup; char *tokencondition; char *tok; uint64_t capset; char *tmptok; int usermatch=0; /* skip leading spaces */ while (isspace(*scan)) scan++; if (*scan == 0 || *scan == '#') //comment continue; tokencap=strtok_r(scan, ":", &tmptok); //printf("CAP %s\n",tokencap); tokenusergroup=strtok_r(NULL, ":\n", &tmptok); //printf("UG %s\n",tokenusergroup); tokencondition=strtok_r(NULL, ":\n", &tmptok); //printf("COND %s\n",tokencondition); if (capset_from_namelist(tokencap, &capset) < 0) continue; if (user_groups == NULL) { ok_caps |= capset; continue; } //printf("CAP %s %d\n",tok,thiscap); while ((tok=strtok_r(tokenusergroup, ",\n ",&tmptok)) != NULL) { //printf("XX %s\n",tok); if (*tok=='@') { if (groupmatch(tok+1, user_groups+1)) { usermatch = 1; break; } } else if (strcmp(tok, user_groups[0]) == 0) { usermatch = 1; break; } tokenusergroup=NULL; } if (usermatch) { if (tokencondition) { if (system_execsa(tokencondition) == 0) ok_caps |= capset; } else ok_caps |= capset; } } fclose(f); if (line) free(line); } /* the capability to read cado.conf is no longer needed */ if (user_groups) lower_cap_dac_read_search(); return ok_caps; } /* set_self_capability sets the capability set needed by cado itself */ int set_self_capability(uint64_t capset) { cap_value_t cap; cap_t caps=cap_init(); int f,rv=-1; for (cap = 0; cap <= CAP_LAST_CAP; cap++) { if (capset & (1ULL << cap)) { if (cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_SET)) { fprintf(stderr, "Cannot set permitted cap %s\n",cap_to_name(cap)); exit(2); } } } if ((f=open("/proc/self/exe",O_RDONLY)) >= 0) { if (cap_set_fd(f,caps) >= 0) rv=0; close(f); } cap_free(caps); return rv; } cado-0.9.5/src/scado.c000066400000000000000000000230471365354605000144740ustar00rootroot00000000000000/* * scado: setuid in capability sauce. * Copyright (C) 2016 Davide Berardi, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EDIT_PAM_MAXTRIES 3 #define SUCCESS 0 /* Generic error */ #define ERROR_EXIT -1 /* Authentication error */ #define ERROR_AUTH -2 const char *tmpdir; #define OPTSTRING "hu:UDle" struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"update", required_argument, NULL, 'u'}, {"update-all", no_argument, NULL, 'U'}, {"delete", no_argument, NULL, 'D'}, {"list", no_argument, NULL, 'l'}, {"edit", no_argument, NULL, 'e'}, }; enum scado_option {none, update, delete, list, edit, toomany}; void usage_n_exit(char *progname, char *message) { fprintf(stderr, "%s - script cado, setuid in capability sauce\n", progname); fprintf(stderr, "usage: %s OPTIONS\n", progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -h --help display help message and exit\n"); fprintf(stderr, " -u --update update the checksum of command\n"); fprintf(stderr, " -U --update-all update the checksum of all commands\n"); fprintf(stderr, " -D --delete delete the scado file\n"); fprintf(stderr, " -l --list print the scado file\n"); fprintf(stderr, " -e --edit edit the scado file\n"); if (message) fprintf(stderr, "\n%s\n",message); exit(ERROR_EXIT); } /* watchdog for garbage collection. the temporary file for scado editing is removed in case of abend */ static int editor_garbage_collect(char *path) { int checkpipe[2]; pid_t childpid; if (pipe(checkpipe)) return -1; /* Start garbage collector * Double fork to avoid zombie processes and to remove the temporary file when it is not needed any more */ if(!(childpid = fork())) { /* Child */ if(!fork()) { /* Grandchild */ if (close(checkpipe[1]) == 0 && setsid() > 0) { char c; if (read(checkpipe[0], &c, 1) == 0) unlink(path); } exit(0); } else exit(0); } waitpid(childpid, NULL, 0); /* (grand) parent */ close(checkpipe[0]); return(checkpipe[1]); } static void editor_garbage_collect_do_not_unlink(int fd) { char c = 'K'; // keep it, any other non-null char would fit. int n = write(fd, &c, 1); (void) n; } /* command line selectable functions */ int scado_none_or_toomany(char *progname, char *username, char *program_path) { usage_n_exit(progname, "select exactly one option among: -u -U -D -l -e"); return 0; } int scado_delete(char *progname, char *username, char *program_path) { char scado_file[PATH_MAX]; if (get_scado_file(username, scado_file) < 0) return ERROR_EXIT; /* Get the authorization. */ if (pam_check(username) != PAM_SUCCESS) { return ERROR_AUTH; } return unlink(scado_file); } int scado_update(char *progname, char *username, char *program_path) { char tmp_file[PATH_MAX]; char scado_file[PATH_MAX]; if (get_scado_file(username, scado_file) < 0) return ERROR_EXIT; /* Get the authorization. */ if (pam_check(username) != PAM_SUCCESS) { return ERROR_AUTH; } if (snprintf(tmp_file, PATH_MAX, "%s/%s", tmpdir, TMP_TEMPLATE) < 0) return ERROR_EXIT; if (copytemp(scado_file, tmp_file) < 0) return ERROR_EXIT; scado_copy_update(tmp_file, scado_file, program_path); if (unlink(tmp_file) < 0) return ERROR_EXIT; return SUCCESS; } int scado_list(char *progname, char *username, char *program_path) { char scado_file[PATH_MAX]; int fd; int outfl = fcntl(STDOUT_FILENO, F_GETFL, 0); if (outfl & O_APPEND) fcntl(STDOUT_FILENO, F_SETFL, outfl & ~O_APPEND); if (get_scado_file(username, scado_file) < 0) return ERROR_EXIT; if ((fd = open(scado_file, O_RDONLY)) < 0) return fd; copyfile(fd, STDOUT_FILENO); close(fd); return SUCCESS; } int scado_edit(char *progname, char *username, char *program_path) { char tmp_file[PATH_MAX]; char scado_file[PATH_MAX]; char *editor; char *args; int status = 0; pid_t pid, xpid; char digest_before[DIGESTSTRLEN + 1]; char digest_after[DIGESTSTRLEN + 1]; int garbage_collect_fd; /* Ignore signals. */ (void) signal(SIGHUP, SIG_IGN); (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); if (get_scado_file(username, scado_file) < 0) return ERROR_EXIT; if (snprintf(tmp_file, PATH_MAX, "%s/%s", tmpdir, TMP_TEMPLATE) < 0) return ERROR_EXIT; if (copytemp_digest(scado_file, tmp_file, digest_before) < 0) { *digest_before = 0; if (errno == ENOENT) { int tmpfd=mkstemp(tmp_file); if (tmpfd < 0) return ERROR_EXIT; if (write(tmpfd, DEFAULT_MESSAGE, sizeof(DEFAULT_MESSAGE)-1) < 0) return ERROR_EXIT; close(tmpfd); } else return ERROR_EXIT; } garbage_collect_fd = editor_garbage_collect(tmp_file); /* Get the editor from the configuration. */ if (((editor = getenv("VISUAL")) == NULL || *editor == '\0') && ((editor = getenv("EDITOR")) == NULL || *editor == '\0')) { editor = EDITOR; } switch (pid = fork()) { case -1: perror("fork"); exit(ERROR_EXIT); case 0: /* XXX secure? */ if (setgid(getgid()) < 0) { perror("setgid(getgid())"); exit(ERROR_EXIT); } if (setuid(getuid()) < 0) { perror("setuid(getuid())"); exit(ERROR_EXIT); } if (asprintf(&args, "%s %s", editor, tmp_file) < 0) { exit(ERROR_EXIT); } execsp(args); perror(editor); exit(ERROR_EXIT); default: /* parent */ break; } /* parent */ for (;;) { xpid = waitpid(pid, &status, 0); if (xpid == -1) { if(errno != EINTR) { fprintf(stderr, "wait pid: error waiting for PID %ld (%s): %s \n", (long) xpid, editor, strerror(errno)); return ERROR_EXIT; } } else if (WIFEXITED(status) && WEXITSTATUS(status)) { fprintf(stderr,"wait pid: exit status"); return ERROR_EXIT; } else if(WIFSIGNALED(status)) { fprintf(stderr, "%s killed: signal %d (%score dumped)\n", editor, WTERMSIG(status), WCOREDUMP(status) ? "": "no "); } else { break; } } /* Restore signals. */ (void) signal(SIGHUP, SIG_DFL); (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); if(compute_digest(tmp_file, digest_after) < 0) return ERROR_EXIT; if (strcmp(digest_before, digest_after) != 0) { int count; /* Get the authorization. */ for (count = 0; count < EDIT_PAM_MAXTRIES; count++) { if (pam_check(username) == PAM_SUCCESS) { break; } if (count < EDIT_PAM_MAXTRIES - 1) fprintf(stderr, "PAM authorization failed\n"); else { editor_garbage_collect_do_not_unlink(garbage_collect_fd); fprintf(stderr, "A copy of the edited scado file has been saved as %s\n", tmp_file); return ERROR_AUTH; } } scado_copy_update(tmp_file, scado_file, NULL); } close(garbage_collect_fd); return SUCCESS; } typedef int (* option_function) (char *progname, char *username, char *program_path); /* array to select the requested option */ option_function option_map[] = { scado_none_or_toomany, scado_update, scado_delete, scado_list, scado_edit, scado_none_or_toomany}; /* update the chosen option. If one was already chosen, switch to "toomany" */ static inline enum scado_option set_option(enum scado_option option, enum scado_option newvalue) { return option == none ? newvalue : toomany; } int main(int argc, char **argv) { struct passwd *pw; char *progname = basename(argv[0]); char *username; char *program_path = ""; /* "" means ALL */ enum scado_option option=none; int rval = 1; if ((pw = getpwuid(getuid())) == NULL) { fprintf(stderr, "Your UID isn't in the passwd file.\n"); exit(ERROR_EXIT); } username = pw->pw_name; if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir== '\0') { tmpdir = TMPDIR; } while(1) { int c = getopt_long(argc, argv, OPTSTRING, long_options, NULL); if (c < 0) break; switch(c) { case 'h': usage_n_exit(progname, NULL); break; case 'u': program_path = optarg; case 'U': option=set_option(option,update); break; case 'D': option=set_option(option,delete); break; case 'l': option=set_option(option,list); break; case 'e': option=set_option(option,edit); break; default: usage_n_exit(progname, NULL); } } if (optind != argc) /* unknown trailing arguments */ usage_n_exit(progname, "unknown trailing arguments"); rval = option_map[option](progname, username, program_path); if (rval == ERROR_AUTH) { fprintf(stderr, "%s: Authorization failure.\n", progname); } else if (rval) { fprintf(stderr, "%s returned: %d, %s\n", progname, rval, strerror(errno)); } return rval; } cado-0.9.5/src/scado_parse.c000066400000000000000000000222451365354605000156650ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli and Davide Berardi, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include #include #include #include #include /* this structure stores the output of the "parsing" of a scado line. a line has (at most) three colon ":" separated fields. all field and end pointers are addresses within the string "line". the i-th field begins at field[i] and terminates just before end[i] */ struct scado_line { char *line; char *field[3]; char *end[3]; }; /* parse a scado line */ static struct scado_line scado_parse(char *line) { struct scado_line out={line, {NULL, NULL, NULL}, {NULL, NULL, NULL}}; char *scan=line; int i; for (i=0; i<3; i++) { while (isspace(*scan)) scan++; out.field[i] = scan; if (*scan == '#' || *scan == 0) goto end; while (*scan != ':') { if (scan[0] == '\\' && scan[1] != 0) scan++; if (*scan == '#' || *scan == 0) goto end; scan++; } out.end[i] = scan; scan++; } return out; end: out.end[i] = scan; return out; } /* true if the current line matches path, false otherwise. scado_path_match manages the escape chars '\' in the scado line without any copy */ static int scado_path_match(const char *path, struct scado_line *line) { char *lpath; for (lpath = line->field[0]; *path && *lpath; path++, lpath++) { if (lpath[0] == '\\' && lpath[1] != 0) lpath++; if (*lpath != *path) break; } return (*path == 0 && (*lpath == 0 || isspace(*lpath) || *lpath == ':')); } /* get the capability set from a scado file line */ static int scado_get_caps(struct scado_line *line, uint64_t *capset) { if (line->field[1]) { size_t caplen = line->end[1] - line->field[1]; char caps[caplen+1]; strncpy(caps,line->field[1],caplen); caps[caplen]=0; return capset_from_namelist(caps, capset); } else return -1; } /* scado_check_digest_syntax returns: -1 if the digest field has syntax errors, 0 if it is an ampty field (but the field exists!) 1 if the line has just two fields (one ':') OR there is a well-formed hex digest */ static int scado_check_digest_syntax(struct scado_line *line) { if (line->field[2]) { int i; size_t digestlen = line->end[2] - line->field[2]; char digest[digestlen+1]; strncpy(digest,line->field[2],digestlen); digest[digestlen]=0; if (*digest == 0) return 0; if (strlen(digest) < DIGESTSTRLEN) return -1; for (i = 0; i < DIGESTSTRLEN && isxdigit(digest[i]); i++) ; if (i < DIGESTSTRLEN) return -1; for (; digest[i] != 0; i++) if (!isspace(digest[i])) return -1; return 1; } else return 1; } /* true if the field exists and it is not empty */ static inline int fieldok(struct scado_line *line, int i) { return line->field[i] && line->end[i] != line->field[i]; } /* some syntax sanity checks */ static int scado_syntax_check(char *path, int lineno, struct scado_line *line) { uint64_t capset; int rv=0; if (line->end[2] && line->end[2][0] == ':') { fprintf(stderr,"%s line %d: extra trailing chars\n", path, lineno); rv = -1; } if (fieldok(line, 0) && line->field[0][0] != '/') { fprintf(stderr,"%s line %d: pathname is not absolute\n", path, lineno); rv = -1; } if (fieldok(line, 0) && !fieldok(line, 1)) { fprintf(stderr,"%s line %d: missing capability set\n", path, lineno); rv = -1; } if (fieldok(line, 1) && scado_get_caps(line, &capset) < 0) { fprintf(stderr,"%s line %d: wrong capability syntax\n", path, lineno); rv = -1; } if (fieldok(line, 2) && scado_check_digest_syntax(line) < 0) { fprintf(stderr,"%s line %d: wrong digest syntax\n", path, lineno); rv = -1; } return rv; } /* clean the path (parsing escape chars) in place */ static void cleanpath(char *path) { char *out=path; while(*path != 0 && !isspace(*path)) { if (path[0] == '\\' && path[1] != 0) path++; *out++ = *path++; } *out=0; } /* update a line */ /* inpath == NULL -> add the digest it if is missing inpath == "" -> update the digest in any case inpath == "/....." (a valid path) -> update the digest if the path of this line matches */ static void scado_update_line(FILE *fout, struct scado_line *line, char *inpath) { if (line->field[2] != NULL) { /* there is the digest field */ size_t pathlen = line->end[0] - line->field[0]; char path[pathlen+1]; /* create a temporary copy of the path on the stack and clean it*/ strncpy(path,line->field[0],pathlen); path[pathlen]=0; cleanpath(path); int digestok=scado_check_digest_syntax(line); if (digestok < 0) // do not update in case of syntax error fprintf(fout, "%s\n", line->line); else if (digestok == 0) { //missing digest -> fill in! char digest[DIGESTSTRLEN + 1]; int len = line->field[2] - line->line; if (compute_digest(path, digest) < 0) fprintf(fout, "%s\n", line->line); else if (line->end[2] != 0) fprintf(fout,"%*.*s%s %s\n",len,len,line->line,digest,line->end[2]); else fprintf(fout,"%*.*s%s\n",len,len,line->line,digest); } else if (inpath && (*inpath == 0 || strcmp(path, inpath) == 0)) { /* if ALL or this path */ char digest[DIGESTSTRLEN + 1]; int len = line->field[2] - line->line; if (compute_digest(path, digest) < 0) fprintf(fout, "%s\n", line->line); else fprintf(fout,"%*.*s%s%s\n",len,len,line->line,digest,line->field[2]+DIGESTSTRLEN); } else fprintf(fout, "%s\n", line->line); } else fprintf(fout, "%s\n", line->line); } /* copy the file from inpath to outpath, updating the digest where required: path == NULL -> add missing digests path == "" -> all */ void scado_copy_update(char *inpath, char *outpath, char *path) { FILE *fin = NULL; mode_t oldmask; FILE *fout; char *line; size_t len=0; int lineno=0; ssize_t n; int fdin; struct stat l_stats; /* Symlinks check */ if ((fdin = open(inpath, O_RDONLY | O_NOFOLLOW)) < 0) { return; } if (fstat(fdin, &l_stats) || l_stats.st_mode == S_IFLNK) { return; } fin = fdopen(fdin, "r"); if (fin == NULL) return; oldmask = umask(077); fout = fopen(outpath, "w"); umask(oldmask); if (fout == NULL) { fclose(fin); return; } while ((n = getline(&line, &len, fin)) > 0) { struct scado_line scado_line; line[n-1] = 0; scado_line = scado_parse(line); lineno++; scado_syntax_check(outpath, lineno, &scado_line); scado_update_line(fout, &scado_line, path); } fclose(fin); fclose(fout); free(line); } /* scan the scado file whose pathname is inpath seeking for the line matching with 'path' scado_path_getinfo returns: -1 in case of error, 0 if the line is missing, 1 if the line has been found */ /* if scado_path_getinfo succeeds, pcapset and digest are set to the set of permitted capabilities and the hash digest, respectively. */ int scado_path_getinfo(char *inpath, const char *path, uint64_t *pcapset, char *digest) { FILE *fin = fopen(inpath, "r"); char *line; size_t len=0; ssize_t n; int lineno=0; int rv = 0; if (fin == NULL) return -1; while ((n = getline(&line, &len, fin)) > 0) { struct scado_line scado_line; line[n-1] = 0; scado_line = scado_parse(line); lineno++; if (scado_path_match(path, &scado_line)) { if (scado_syntax_check(inpath, lineno, &scado_line) == 0) { if (scado_get_caps(&scado_line, pcapset) >= 0) { if (scado_line.field[2]) { int i; for (i = 0; i < DIGESTSTRLEN ; i++) digest[i] = tolower(scado_line.field[2][i]); digest[i] = 0; rv = 1; } else { digest[0] = 0; rv = 1; } } } break; } } fclose(fin); free(line); return rv; } #if 0 int main() { char *line; size_t len=0; struct scado_line s; int lineno=0; while (1) { ssize_t n=getline(&line, &len, stdin); line[n-1]=0; lineno++; s=scado_parse(line); printf("L %s\n",s.line); printf("P %s|%s\n",s.field[0],s.end[0]); printf("C %s|%s\n",s.field[1],s.end[1]); printf("H %s|%s\n",s.field[2],s.end[2]); uint64_t cap=0; printf("%d ",scado_get_caps(&s,&cap)); printf("%lx\n",cap); scado_syntax_check(lineno, &s); printf("NULL->");fflush(stdout); scado_update_line(1, &s, NULL); printf("ALL ->");fflush(stdout); scado_update_line(1, &s, ""); printf("bash ->");fflush(stdout); scado_update_line(1, &s, "/bin/bash"); } } #endif #if 0 int main(int argc, char *argv[]) { scado_update(argv[1], argv[2], argv[3]); } #endif #if 0 int main(int argc, char *argv[]) { char digest[DIGESTSTRLEN + 1]; uint64_t capset; if (scado_path_getinfo(argv[1], argv[2], &capset, digest)) { printf("%lx %s\n",capset,digest); } } #endif cado-0.9.5/src/set_ambient_cap.c000066400000000000000000000062771365354605000165260ustar00rootroot00000000000000/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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, see . * */ #include #include #include #include #include /* just in case prctl.h is not providing these definitions */ #ifndef PR_CAP_AMBIENT #define PR_CAP_AMBIENT 47 #endif #ifndef PR_CAP_AMBIENT_RAISE #define PR_CAP_AMBIENT_RAISE 2 #endif #ifndef PR_CAP_AMBIENT_LOWER #define PR_CAP_AMBIENT_LOWER 3 #endif #ifndef PR_CAP_AMBIENT_CLEAR_ALL #define PR_CAP_AMBIENT_CLEAR_ALL 4 #endif /* set the ambient capabilities to match the bitmap capset. the capability #k is active if and only if the (k+1)-th least significative bit in capset is 1. (i.e. if and only if (capset & (1ULL << k)) is not zero. */ void set_ambient_cap(uint64_t capset) { cap_value_t cap; cap_t caps=cap_get_proc(); for (cap = 0; cap <= CAP_LAST_CAP; cap++) { int op = (capset & (1ULL << cap)) ? CAP_SET : CAP_CLEAR; if (cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, op)) { fprintf(stderr, "Cannot %s inheritable cap %s\n",op==CAP_SET?"set":"clear",cap_to_name(cap)); exit(2); } } cap_set_proc(caps); cap_free(caps); for (cap = 0; cap <= CAP_LAST_CAP; cap++) { int op = (capset & (1ULL << cap)) ? PR_CAP_AMBIENT_RAISE : PR_CAP_AMBIENT_LOWER; if (prctl(PR_CAP_AMBIENT, op, cap, 0, 0)) { perror("Cannot set cap"); exit(1); } } } /* drop the capabilities of the capset */ void drop_ambient_cap(uint64_t capset) { cap_value_t cap; cap_t caps=cap_get_proc(); for (cap = 0; cap <= CAP_LAST_CAP; cap++) { if (capset & (1ULL << cap)) { if (cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR)) { fprintf(stderr, "Cannot clear inheritable cap %s\n",cap_to_name(cap)); exit(2); } } } cap_set_proc(caps); cap_free(caps); for (cap = 0; cap <= CAP_LAST_CAP; cap++) { if ((capset & (1ULL << cap))) { if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, cap, 0, 0)) { perror("Cannot set cap"); exit(1); } } } } int drop_all_ambient_cap(void) { return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); } /* turn cap_dac_read_search on and off to have "extra" powers only when needed */ void raise_cap_dac_read_search(void) { cap_value_t cap=CAP_DAC_READ_SEARCH; cap_t caps=cap_get_proc(); cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET); cap_set_proc(caps); } void lower_cap_dac_read_search(void) { cap_value_t cap=CAP_DAC_READ_SEARCH; cap_t caps=cap_get_proc(); cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_CLEAR); cap_set_proc(caps); }