"Fossies" - the Fresh Open Source Software Archive

Member "jailkit-2.21/src/jk_lsh.c" (11 Mar 2016, 10220 Bytes) of package /linux/privat/jailkit-2.21.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "jk_lsh.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.19_vs_2.20.

    1 /*
    2 Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2015, 2016 Olivier Sessink
    3 All rights reserved.
    4 
    5 This file is available under two licences, at your own choice
    6 
    7 --------
    8 Redistribution and use in source and binary forms, with or without
    9 modification, are permitted provided that the following conditions
   10 are met:
   11   * Redistributions of source code must retain the above copyright
   12     notice, this list of conditions and the following disclaimer.
   13   * Redistributions in binary form must reproduce the above
   14     copyright notice, this list of conditions and the following
   15     disclaimer in the documentation and/or other materials provided
   16     with the distribution.
   17   * The names of its contributors may not be used to endorse or
   18     promote products derived from this software without specific
   19     prior written permission.
   20 
   21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32 POSSIBILITY OF SUCH DAMAGE.
   33 
   34 --------
   35 
   36 This software is free software; you can redistribute it and/or
   37 modify it under the terms of the GNU Library General Public
   38 License as published by the Free Software Foundation; either
   39 version 2 of the License, or (at your option) any later version.
   40 
   41 This software is distributed in the hope that it will be useful,
   42 but WITHOUT ANY WARRANTY; without even the implied warranty of
   43 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   44 Library General Public License for more details.
   45 
   46 You should have received a copy of the GNU Library General Public
   47 License along with this library; if not, write to the
   48 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
   49 Boston, MA  02110-1301, USA.
   50 
   51  */
   52 
   53 /*
   54  * Limited shell, will only execute files that are configured in /etc/jailkit/jk_lsh.ini
   55  */
   56 #include "config.h"
   57 
   58 #include <string.h>
   59 #include <ctype.h>
   60 #include <unistd.h>
   61 #include <sys/types.h>
   62 #include <sys/stat.h>
   63 #include <syslog.h>
   64 #include <unistd.h>
   65 #include <pwd.h>
   66 #include <grp.h>
   67 #include <stdlib.h>
   68 #include <errno.h>
   69 
   70 #ifdef HAVE_WORDEXP_H
   71 #include <wordexp.h>
   72 #else
   73 #include "wordexp.h"
   74 /* needed to link wordexp.o with the executable */
   75 int libc_argc;
   76 char **libc_argv;
   77 
   78 #endif
   79 
   80 #define PROGRAMNAME "jk_lsh"
   81 #define CONFIGFILE INIPREFIX"/jk_lsh.ini"
   82 
   83 /* #define DEBUG */
   84 
   85 #include "jk_lib.h"
   86 #include "iniparser.h"
   87 
   88 /* doesn't compile on FreeBSD without this */
   89 extern char **environ;
   90 
   91 static int executable_is_allowed(Tiniparser *parser, const char *section, const char *executable, int position) {
   92     int klen;
   93     char **arr, **tmp;
   94     char buffer[1024];
   95     klen = iniparser_get_string_at_position(parser, section, "executables",position, buffer, 1024);
   96     if (!klen) {
   97         syslog(LOG_ERR, "section %s does not have a key executables", section);
   98         exit(5);
   99     }
  100     arr = tmp = explode_string(buffer, ',');
  101     while (tmp && *tmp) {
  102         DEBUG_MSG("comparing '%s' and '%s'\n",*tmp,executable);
  103         if (strcmp(*tmp,executable)==0) {
  104             free_array(arr);
  105             return 1;
  106         }
  107         tmp++;
  108     }
  109     free_array(arr);
  110     return 0;
  111 }
  112 
  113 static char *expand_executable_w_path(const char *executable, char **allowed_paths) {
  114     DEBUG_LOG("expand_executable_w_path, executable=%s",executable);
  115     if (executable[0] == '/' && file_exists(executable)) {
  116         return strdup(executable);
  117     }
  118     if (!allowed_paths) {
  119         return NULL;
  120     } else {
  121         char **path = allowed_paths;
  122         int elen = strlen(executable);
  123         while (*path) {
  124             char *newpath;
  125             int tlen = strlen(*path);
  126             newpath = malloc((elen+tlen+2)*sizeof(char));
  127             memset(newpath, 0, (elen+tlen+2)*sizeof(char));
  128             newpath = strncpy(newpath, *path, tlen);
  129             if (*(*path+tlen-1) != '/') {
  130                 newpath = strcat(newpath, "/");
  131                 DEBUG_LOG("newpath=%s",newpath);
  132             }
  133             newpath = strncat(newpath, executable, elen);
  134             if (file_exists(newpath)) {
  135                 DEBUG_MSG("file %s exists\n",newpath);
  136                 return newpath;
  137             }
  138             free(newpath);
  139             path++;
  140         }
  141     }
  142     syslog(LOG_ERR,"the requested executable %s is not found\n",executable);
  143     return NULL;
  144 }
  145 /* returns a NULL terminated array of strings */
  146 char **expand_newargv(char *string) {
  147     wordexp_t p;
  148     wordexp(string, &p, 0);
  149     return p.we_wordv;
  150 }
  151 
  152 int main (int argc, char **argv) {
  153     Tiniparser *parser;
  154     const char *section;
  155     unsigned int section_pos, umaskval;
  156     struct passwd *pw;
  157     char *new, buffer[1024], *tmp=NULL, *user=NULL;
  158     char **paths = NULL;
  159     char ** newargv;
  160     struct group *gr;
  161     char *groupsec=NULL;
  162     int retval;
  163     char *logstring;
  164 
  165     DEBUG_MSG(PROGRAMNAME" version "VERSION", started\n");
  166 #ifndef HAVE_WORDEXP_H
  167     libc_argc = argc;
  168     libc_argv = argv;
  169 #endif
  170     /* open the log facility */
  171     openlog(PROGRAMNAME, LOG_PID, LOG_AUTH);
  172     syslog(LOG_INFO, PROGRAMNAME" version "VERSION", started");
  173 
  174     DEBUG_MSG(PROGRAMNAME" log started\n");
  175     tmp = getenv("USER");
  176     if (tmp && strlen(tmp)) {
  177         user = strdup(tmp);
  178     }
  179     if (user) {
  180         pw = getpwnam(user);
  181     } else {
  182         pw = getpwuid(getuid());
  183     }
  184     if (!pw) {
  185         if (user)
  186             syslog(LOG_ERR, "cannot find user info for USER %s: %s", user, strerror(errno));
  187         else
  188             syslog(LOG_ERR, "cannot find user info for uid %u: %s", getuid(), strerror(errno));
  189         DEBUG_MSG(PROGRAMNAME" cannot find user name for uid %u: %s", getuid(), strerror(errno));
  190         exit(2);
  191     }
  192     if (user && pw->pw_uid != getuid()) {
  193         syslog(LOG_ERR, "abort, running as UID %d, but environment variable USER %s has UID %d", getuid(), user, pw->pw_uid);
  194         exit(2);
  195     }
  196     gr = getgrgid(getgid());
  197     if (!gr || !gr->gr_name || gr->gr_name[0]=='\0') {
  198         syslog(LOG_ERR, "cannot find group name for gid %u: %s", getuid(), strerror(errno));
  199         DEBUG_MSG(PROGRAMNAME" cannot find group name for uid %u: %s", getuid(), strerror(errno));
  200         exit(3);
  201     }
  202 
  203     /* the last argument should be the commandstring, and the one before should be -c
  204     before that there could be an argument like --login that we simply ignore */
  205     if (argc < 3 || strcmp(argv[argc - 2],"-c")!=0) {
  206         char *requeststring = implode_array(argv,argc," ");
  207         DEBUG_MSG("WARNING: user %s (%u) tried to get an interactive shell session (%s), which is never allowed by jk_lsh\n", pw->pw_name, getuid(),requeststring);
  208         syslog(LOG_ERR, "WARNING: user %s (%u) tried to get an interactive shell session (%s), which is never allowed by jk_lsh", pw->pw_name, getuid(),requeststring);
  209         free(requeststring);
  210         exit(7);
  211     }
  212 
  213     /* start the config parser */
  214     parser = new_iniparser(CONFIGFILE);
  215     if (!parser) {
  216         syslog(LOG_ERR, "configfile "CONFIGFILE" is not available");
  217         DEBUG_MSG(PROGRAMNAME" configfile missing\n");
  218         exit(1);
  219     }
  220     /* check if this user has a section. asprintf() is a GNU extension which is
  221     not available on Solaris */
  222     groupsec = strcat(strcpy(malloc0(strlen(gr->gr_name)+7), "group "), gr->gr_name);
  223     if (iniparser_has_section(parser, pw->pw_name)) {
  224         section = pw->pw_name;
  225     } else if (iniparser_has_section(parser, groupsec)) {
  226         section = groupsec;
  227     } else if (iniparser_has_section(parser, "DEFAULT")) {
  228         section = "DEFAULT";
  229     } else {
  230         syslog(LOG_ERR, "did neither find a section '%s', nor 'group %s' nor 'DEFAULT' in configfile "CONFIGFILE, pw->pw_name, gr->gr_name);
  231         exit(3);
  232     }
  233     section_pos = iniparser_get_position(parser) - strlen(section) - 2;
  234     section_pos = section_pos >= 0 ? section_pos : 0;
  235     DEBUG_MSG("using section %s\n",section);
  236 
  237     DEBUG_MSG("setting umask\n");
  238     umaskval = iniparser_get_octalint_at_position(parser, section, "umask", section_pos, 0000);
  239     if (umaskval != -1) {
  240         mode_t oldumask;
  241         oldumask = umask(umaskval);
  242         /*syslog(LOG_DEBUG, "changing umask from 0%o to 0%o", oldumask, umaskval);*/
  243     }
  244     if (iniparser_get_string_at_position(parser, section, "environment", section_pos, buffer, 1024) > 0) {
  245         char **envs, **tmp;
  246         envs = explode_string(buffer, ',');
  247         tmp = envs;
  248         while (*tmp) {
  249             char **keyval = explode_string(*tmp, '=');
  250             if (keyval[0] && keyval[1] && keyval[2]==NULL) {
  251                 setenv(keyval[0],keyval[1],1);
  252             }
  253             free_array(keyval);
  254             tmp++;
  255         }
  256         free_array(envs);
  257     }
  258 
  259     DEBUG_MSG("exploding string '%s'\n",argv[argc-1]);
  260     if (iniparser_get_int_at_position(parser, section, "allow_word_expansion", section_pos, 0)) {
  261         newargv = expand_newargv(argv[argc-1]);
  262     } else {
  263         newargv = explode_string(argv[argc-1], ' ');
  264     }
  265     if (iniparser_get_string_at_position(parser, section, "paths", section_pos, buffer, 1024) > 0) {
  266         DEBUG_LOG("paths, buffer=%s\n",buffer);
  267         paths = explode_string(buffer, ',');
  268     } else {
  269         DEBUG_LOG("no key paths found\n");
  270     }
  271 
  272     DEBUG_LOG("paths=%p, newargv[0]=%s",paths,newargv[0]);
  273     new = expand_executable_w_path(newargv[0], paths);
  274     free_array(paths);
  275     if (new) {
  276         free(newargv[0]);
  277         newargv[0] = new;
  278     }
  279     if (!executable_is_allowed(parser, section, newargv[0],section_pos)) {
  280         char *logstring;
  281         logstring = implode_array(newargv,-1," ");
  282         DEBUG_MSG("WARNING: user %s (%u) tried to run '%s'\n", pw->pw_name, getuid(),newargv[0]);
  283         syslog(LOG_ERR, "WARNING: user %s (%u) tried to run '%s', which is not allowed according to "CONFIGFILE, pw->pw_name, getuid(),logstring);
  284         free(logstring);
  285         exit(4);
  286     }
  287 
  288     iniparser_close(parser);
  289 
  290     logstring = implode_array(newargv,-1," ");
  291     DEBUG_MSG("executing command '%s' for user %s (%u)\n", logstring,pw->pw_name, getuid());
  292     syslog(LOG_INFO, "executing command '%s' for user %s (%u)", logstring,pw->pw_name, getuid());
  293     free(logstring);
  294     retval = execve(newargv[0],newargv,environ);
  295     DEBUG_MSG("errno=%d, error=%s\n",errno,strerror(errno));
  296     DEBUG_MSG("execve() command '%s' returned %d\n", newargv[0], retval);
  297     syslog(LOG_ERR, "WARNING: running %s failed for user %s (%u): %s", newargv[0],pw->pw_name, getuid(), strerror(retval));
  298     syslog(LOG_ERR, "WARNING: check the permissions and libraries for %s", newargv[0]);
  299     return retval;
  300 }