"Fossies" - the Fresh Open Source Software Archive

Member "mpm-itk-2.4.7-04/mpm_itk.c" (14 Feb 2016, 25417 Bytes) of package /linux/www/apache_httpd_modules/mpm-itk-2.4.7-04.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 "mpm_itk.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.4.7-03_vs_2.4.7-04.

    1 /* Licensed to the Apache Software Foundation (ASF) under one or more
    2  * contributor license agreements.  See the NOTICE file distributed with
    3  * this work for additional information regarding copyright ownership.
    4  * The ASF licenses this file to You under the Apache License, Version 2.0
    5  * (the "License"); you may not use this file except in compliance with
    6  * the License.  You may obtain a copy of the License at
    7  *
    8  *     http://www.apache.org/licenses/LICENSE-2.0
    9  *
   10  * Unless required by applicable law or agreed to in writing, software
   11  * distributed under the License is distributed on an "AS IS" BASIS,
   12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13  * See the License for the specific language governing permissions and
   14  * limitations under the License.
   15  * 
   16  * Portions copyright 2005-2016 Steinar H. Gunderson <sgunderson@bigfoot.com>.
   17  * Licensed under the same terms as the rest of Apache.
   18  *
   19  * Portions copyright 2008 Knut Auvor Grythe <knut@auvor.no>.
   20  * Licensed under the same terms as the rest of Apache.
   21  */
   22 
   23 #define MPMITK_VERSION "2.4.7-04"
   24 
   25 #include "config.h"
   26 
   27 #include "apr.h"
   28 #include "apr_portable.h"
   29 #include "apr_strings.h"
   30 #include "apr_thread_proc.h"
   31 #include "apr_signal.h"
   32 
   33 # define _DBG(text,par...) \
   34     ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, \
   35                 "(itkmpm: pid=%d uid=%d, gid=%d) %s(): " text, \
   36                 getpid(), getuid(), getgid(), __FUNCTION__, par)
   37 
   38 #define APR_WANT_STDIO
   39 #define APR_WANT_STRFUNC
   40 #include "apr_want.h"
   41 
   42 #if APR_HAVE_UNISTD_H
   43 #include <unistd.h>
   44 #endif
   45 #if APR_HAVE_SYS_TYPES_H
   46 #include <sys/types.h>
   47 #endif
   48 #include <pwd.h>
   49 #include <grp.h>
   50 
   51 #include "ap_config.h"
   52 #include "httpd.h"
   53 #include "http_main.h"
   54 #include "http_log.h"
   55 #include "http_config.h"
   56 #include "http_core.h"          /* for get_remote_host */
   57 #include "http_connection.h"
   58 #include "http_request.h"
   59 #include "scoreboard.h"
   60 #include "ap_mpm.h"
   61 #include "util_mutex.h"
   62 #include "mpm_common.h"
   63 #include "unixd.h"
   64 #include "ap_listen.h"
   65 #include "ap_mmn.h"
   66 #include "apr_poll.h"
   67 #include "ap_expr.h"
   68 
   69 #include "seccomp.h"
   70 
   71 #ifdef HAVE_TIME_H
   72 #include <time.h>
   73 #endif
   74 #ifdef HAVE_SYS_PROCESSOR_H
   75 #include <sys/processor.h> /* for bindprocessor() */
   76 #endif
   77 
   78 #include <signal.h>
   79 #include <sys/times.h>
   80 
   81 #if HAVE_LIBCAP
   82 #include <sys/prctl.h>
   83 #include <sys/capability.h>
   84 #endif
   85 
   86 
   87 /* config globals */
   88 
   89 static uid_t ap_itk_min_uid=1;
   90 static uid_t ap_itk_max_uid=UINT_MAX;
   91 static gid_t ap_itk_min_gid=1;
   92 static gid_t ap_itk_max_gid=UINT_MAX;
   93 
   94 #if HAVE_LIBCAP
   95 static int ap_itk_enable_caps=1;
   96 #endif
   97 
   98 module AP_MODULE_DECLARE_DATA mpm_itk_module;
   99 
  100 #define UNSET_NICE_VALUE 100
  101 
  102 typedef struct
  103 {
  104     uid_t uid;
  105     gid_t gid;
  106     char *username;
  107     int nice_value;
  108     ap_expr_info_t *uid_expr;
  109     ap_expr_info_t *gid_expr;
  110 } itk_per_dir_conf;
  111 
  112 typedef struct
  113 {
  114     int max_clients_vhost;
  115 } itk_server_conf;
  116 
  117 AP_DECLARE_DATA int ap_has_irreversibly_setuid = 0;
  118 
  119 /* Only in use if not enabling capabilities. */
  120 AP_DECLARE_DATA uid_t saved_unixd_uid = -1;
  121 AP_DECLARE_DATA gid_t saved_unixd_gid = -1;
  122 
  123 static int itk_pre_drop_privileges(apr_pool_t *pool, server_rec *s)
  124 {
  125 #if HAVE_LIBCAP
  126     if (ap_itk_enable_caps) {
  127         /* mod_unixd will drop down to a normal user. This means that even if an
  128          * attacker manage to execute code before setuid(), he/she cannot write to
  129          * files owned by uid 0, such as /etc/crontab. We'll need to keep our extra
  130          * privileges, though, since we need them for the actual query processing
  131          * later, so specify that we'll keep them across the setuid that is soon to
  132          * come.
  133          *
  134          * Note that since we still have CAP_SETUID, an attacker can setuid(0)
  135          * and get around this. Thus, we disallow setuid(0) if the platform
  136          * allows it.
  137          */
  138         restrict_setuid_range(ap_itk_min_uid, ap_itk_max_uid, ap_itk_min_gid, ap_itk_max_gid);
  139         if (prctl(PR_SET_KEEPCAPS, 1)) {
  140             ap_log_error(APLOG_MARK, APLOG_EMERG, errno, NULL, "prctl(PR_SET_KEEPCAPS, 1) failed");
  141             exit(APEXIT_CHILDFATAL);
  142         }
  143         return OK;
  144     }
  145 #endif
  146     restrict_setuid_range(ap_itk_min_uid, ap_itk_max_uid, ap_itk_min_gid, ap_itk_max_gid);
  147     /* Fiddle with mod_unixd's structures so that it doesn't drop uid 0;
  148      * we need that, since we don't have capabilities.
  149      */
  150     saved_unixd_uid = ap_unixd_config.user_id;
  151     saved_unixd_gid = ap_unixd_config.group_id;
  152     ap_unixd_config.user_id = 0;
  153     ap_unixd_config.group_id = 0;
  154     return OK;
  155 }
  156 
  157 static int itk_post_drop_privileges(apr_pool_t *pool, server_rec *s)
  158 {
  159 #if HAVE_LIBCAP
  160     if (ap_itk_enable_caps) {
  161         cap_t caps;
  162         cap_value_t suidcaps[] = {
  163             CAP_SETUID,
  164             CAP_SETGID,
  165             CAP_DAC_READ_SEARCH,
  166             CAP_SYS_NICE,
  167         };
  168 
  169         /* We don't need to keep capabilities across setuid anymore, so stop that. */
  170         if (prctl(PR_SET_KEEPCAPS, 0)) {
  171             ap_log_error(APLOG_MARK, APLOG_EMERG, errno, NULL, "prctl(PR_SET_KEEPCAPS, 0) failed");
  172             exit(APEXIT_CHILDFATAL);
  173         }
  174 
  175         /* Now drop as many privileges as we can. We'll still
  176          * access files with uid=0, and we can setuid() to anything, but
  177          * at least there's tons of other evilness (like loading kernel
  178          * modules) we can't do directly. (The setuid() capability will
  179          * go away automatically when we setuid() or exec() -- the former
  180          * is likely to come first.)
  181          */
  182         caps = cap_init();
  183         cap_clear(caps);
  184         cap_set_flag(caps, CAP_PERMITTED, sizeof(suidcaps)/sizeof(cap_value_t), suidcaps, CAP_SET);
  185         cap_set_flag(caps, CAP_EFFECTIVE, sizeof(suidcaps)/sizeof(cap_value_t), suidcaps, CAP_SET);
  186         cap_set_proc(caps);
  187         cap_free(caps);
  188         return OK;
  189     }
  190 #endif
  191     // Restore the configured unixd uid/gid.
  192     ap_unixd_config.user_id = saved_unixd_uid;
  193     ap_unixd_config.group_id = saved_unixd_gid;
  194     return OK;
  195 }
  196 
  197 static int have_forked = 0;
  198 
  199 int itk_fork_process(conn_rec *c)
  200 {
  201     if (have_forked) {
  202          return DECLINED;
  203     }
  204 
  205     pid_t pid = fork(), child_pid;
  206     int status;
  207     switch (pid) {
  208     case -1:
  209     ap_log_error(APLOG_MARK, APLOG_ERR, errno, NULL, "fork: Unable to fork new process");
  210     return HTTP_INTERNAL_SERVER_ERROR;
  211     case 0:
  212         /* Child; runs processing as usual, then dies.
  213          * This is a bit tricky in that we need to run ap_run_process_connection()
  214          * even though we are a process_connection hook ourselves! That is the only
  215          * way we can exit cleanly after the hook is done. Thus, we set have_forked
  216          * to signal that we don't want to end up in infinite recursion.
  217          */
  218         have_forked = 1;
  219         ap_close_listeners();
  220         ap_run_process_connection(c);
  221         ap_lingering_close(c);
  222     exit(0);
  223     default: /* parent; just wait for child to be done */
  224     do {
  225         child_pid = waitpid(pid, &status, 0);
  226     } while (child_pid == -1 && errno == EINTR);
  227 
  228     if (child_pid != pid || !WIFEXITED(status)) {
  229         if (WIFSIGNALED(status)) {
  230         ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "child died with signal %u", WTERMSIG(status));
  231         } else if (WEXITSTATUS(status) != 0) {
  232         ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "child exited with non-zero exit status %u", WEXITSTATUS(status));
  233         } else {
  234         ap_log_error(APLOG_MARK, APLOG_ERR, errno, NULL, "waitpid() failed");
  235         }
  236         exit(1);
  237     }
  238 
  239         /*
  240      * It is important that ap_lingering_close() is called in the child
  241      * and not here, since some modules (like mod_ssl) needs to know the state
  242      * from earlier in the connection to be able to finish correctly.
  243      * However, we close the socket itself here so that we don't keep a
  244      * reference to it around, and then set the socket pointer to NULL so
  245      * that when prefork tries to close it, it goes into early exit.
  246      */
  247     apr_socket_close(ap_get_conn_socket(c));
  248     ap_set_core_module_config(c->conn_config, NULL);
  249 
  250         /* make sure the MPM does not process this connection */
  251     return OK;
  252     }
  253 }
  254 
  255 static int itk_init_handler(apr_pool_t *p, apr_pool_t *plog,
  256                             apr_pool_t *ptemp, server_rec *s)
  257 {
  258     int threaded;
  259     int ret = ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded) != APR_SUCCESS;
  260     if (ret != APR_SUCCESS || threaded) {
  261         ap_log_perror(APLOG_MARK, APLOG_CRIT, ret, ptemp,
  262                       "mpm-itk cannot use threaded MPMs; please use prefork.");
  263         return HTTP_INTERNAL_SERVER_ERROR;
  264     }
  265 
  266     ap_add_version_component(p, "mpm-itk/" MPMITK_VERSION);
  267     return OK;
  268 }
  269 
  270 static int itk_post_perdir_config(request_rec *r)
  271 {
  272     uid_t wanted_uid;
  273     gid_t wanted_gid;
  274     const char *wanted_username;
  275     int err = 0;
  276 
  277     itk_server_conf *sconf =
  278         (itk_server_conf *) ap_get_module_config(r->server->module_config, &mpm_itk_module);
  279     itk_per_dir_conf *dconf;
  280 
  281     /* Enforce MaxClientsVhost. */
  282     if (sconf->max_clients_vhost > 0) {
  283         worker_score *ws;
  284         char my_vhost[sizeof(ws->vhost)];
  285         apr_snprintf(my_vhost, sizeof(my_vhost), "%s:%d",
  286                      r->server->server_hostname,
  287                      r->connection->local_addr->port);
  288 
  289         int daemons_limit; 
  290         ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &daemons_limit);
  291 
  292         int i, num_other_servers = 0;
  293         for (i = 0; i < daemons_limit; ++i) {
  294             worker_score *ws = ap_get_scoreboard_worker_from_indexes(i, 0);
  295             if (ws->status >= SERVER_BUSY_READ && strcmp(ws->vhost, my_vhost) == 0)
  296                 ++num_other_servers;
  297         }
  298 
  299         if (num_other_servers > sconf->max_clients_vhost) {
  300             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, \
  301                 "MaxClientsVhost reached for %s, refusing client.",
  302                 my_vhost);
  303             return HTTP_SERVICE_UNAVAILABLE;
  304         }
  305     }
  306 
  307     dconf = (itk_per_dir_conf *) ap_get_module_config(r->per_dir_config, &mpm_itk_module);
  308     if (dconf->nice_value != UNSET_NICE_VALUE &&
  309         setpriority(PRIO_PROCESS, 0, dconf->nice_value)) {
  310         _DBG("setpriority(): %s", strerror(errno));
  311         err = 1;
  312     }
  313 
  314     wanted_uid = dconf->uid;
  315     wanted_gid = dconf->gid;
  316     wanted_username = dconf->username;
  317 
  318     if (wanted_uid == -1 || wanted_gid == -1) {
  319         wanted_uid = ap_unixd_config.user_id;
  320         wanted_gid = ap_unixd_config.group_id;
  321         wanted_username = ap_unixd_config.user_name;
  322     }
  323 
  324     /* AssignUserIDExpr and AssignGroupIDExpr override AssignUserID and defaults. */
  325     if (dconf->uid_expr != NULL) {
  326       struct passwd *ent;
  327       const char *err;
  328       wanted_username = ap_expr_str_exec(r, dconf->uid_expr, &err);
  329       if (err) {
  330         ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, \
  331             "Error while parsing AssignUserIDExpr expression: %s",
  332             err);
  333         return HTTP_INTERNAL_SERVER_ERROR;
  334       }
  335 
  336       if (!(ent = getpwnam(wanted_username))) {
  337         ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, \
  338             "AssignUserIDExpr returned '%s', which is not a valid user name",
  339             wanted_username);
  340         return HTTP_INTERNAL_SERVER_ERROR;
  341       }
  342 
  343       wanted_uid = ent->pw_uid;
  344     }
  345     if (dconf->gid_expr != NULL) {
  346       struct group *ent;
  347       const char *err;
  348       const char *wanted_groupname = ap_expr_str_exec(r, dconf->gid_expr, &err);
  349       if (err) {
  350         ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, \
  351             "Error while parsing AssignGroupIDExpr expression: %s",
  352             err);
  353         return HTTP_INTERNAL_SERVER_ERROR;
  354       }
  355 
  356       if (!(ent = getgrnam(wanted_groupname))) {
  357         ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, \
  358             "AssignGroupIDExpr returned '%s', which is not a valid group name",
  359             wanted_groupname);
  360         return HTTP_INTERNAL_SERVER_ERROR;
  361       }
  362 
  363       wanted_gid = ent->gr_gid;
  364     }
  365 
  366     /* setuid() at least on the first request, but from there only if we need to change anything.
  367      * (Those requests will almost certainly fail.)
  368      */
  369     if ((!err && !ap_has_irreversibly_setuid) || wanted_uid != getuid() || wanted_gid != getgid()) {
  370         if (setgid(wanted_gid)) {
  371             _DBG("setgid(%d): %s", wanted_gid, strerror(errno));
  372             if (wanted_gid < ap_itk_min_gid || wanted_gid > ap_itk_max_gid) {
  373                 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
  374                              "This is most likely due to the current LimitGIDRange setting.");
  375             }
  376             err = 1;
  377         } else if (initgroups(wanted_username, wanted_gid)) {
  378             _DBG("initgroups(%s, %d): %s", wanted_username, wanted_gid, strerror(errno));
  379             err = 1;
  380         } else if (setuid(wanted_uid)) {
  381             _DBG("setuid(%d): %s", wanted_uid, strerror(errno));
  382             if (wanted_uid < ap_itk_min_uid || wanted_uid > ap_itk_max_uid) {
  383                 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
  384                              "This is most likely due to the current LimitUIDRange setting.");
  385             }
  386             err = 1;
  387         } else {
  388 #if HAVE_LIBCAP
  389             if (ap_itk_enable_caps) {
  390                 /* Drop our remaining privileges. Normally setuid() would do this
  391                  * for us, but since we were previously not uid 0 (just a normal
  392                  * user with CAP_SETUID), we need to do it ourselves.
  393                  */
  394                 cap_t caps;
  395                 caps = cap_init();
  396                 cap_clear(caps);
  397                 cap_set_proc(caps);
  398                 cap_free(caps);
  399             }
  400 #endif
  401             ap_has_irreversibly_setuid = 1;
  402         }
  403     }
  404 
  405     if (err) {
  406         if (ap_has_irreversibly_setuid) {
  407             /* Most likely a case of switching uid/gid within a persistent
  408              * connection; the RFCs allow us to just close the connection
  409              * at anytime, so we excercise our right. :-)
  410              */
  411             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, \
  412                 "Couldn't set uid/gid/priority, closing connection.");
  413             ap_lingering_close(r->connection);
  414             exit(0);
  415         } else {
  416             /* Something went wrong even this is the first request.
  417              * We need to notify the user.
  418              */
  419             return HTTP_INTERNAL_SERVER_ERROR;
  420         }
  421     }
  422 
  423     return OK;
  424 }
  425 
  426 /*
  427  * If we are in a persistent connection, we might end up in a state
  428  * where we can no longer read .htaccess files because we have already
  429  * setuid(). This can either be because the previous request was for
  430  * another vhost (basically the same problem as when setuid() fails in
  431  * itk.c), or it can be because a .htaccess file is readable only by
  432  * root.
  433  *
  434  * In any case, we don't want to give out a 403, since the request has
  435  * a very real chance of succeeding on a fresh connection (where
  436  * presumably uid=0). Thus, we give up serving the request on this
  437  * TCP connection, and do a hard close of the socket. As long as we're
  438  * in a persistent connection (and there _should_ not be a way this
  439  * would happen on the first request in a connection, save for subrequests,
  440  * which we special-case), this is allowed, as it is what happens on
  441  * a timeout. The browser will simply open a new connection and try
  442  * again (there's of course a performance hit, though, both due to
  443  * the new connection setup and the fork() of a new server child).
  444  */
  445 static apr_status_t itk_dirwalk_stat(apr_finfo_t *finfo, request_rec *r,
  446                                      apr_int32_t wanted)
  447 {
  448     apr_status_t status = apr_stat(finfo, r->filename, wanted, r->pool);
  449     if (ap_has_irreversibly_setuid && r->main == NULL && APR_STATUS_IS_EACCES(status)) {
  450          ap_log_rerror(APLOG_MARK, APLOG_WARNING, status, r,
  451                        "Couldn't read %s, closing connection.",
  452                        r->filename);
  453          ap_lingering_close(r->connection);
  454      exit(0);
  455     }
  456     return status;
  457 }
  458 
  459 /* See itk_dirwalk_stat() for rationale. */
  460 static apr_status_t itk_open_htaccess(request_rec *r,
  461                                       const char *dir_name, const char *access_name,
  462                                       ap_configfile_t **conffile, const char **full_name)
  463 {
  464     int status;
  465          
  466     if (!ap_has_irreversibly_setuid || r->main != NULL) {
  467         return AP_DECLINED;
  468     }
  469 
  470     *full_name = ap_make_full_path(r->pool, dir_name, access_name);
  471     status = ap_pcfg_openfile(conffile, r->pool, *full_name);
  472 
  473     if (APR_STATUS_IS_EACCES(status)) {
  474          ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, r,
  475                        "Couldn't read %s, closing connection.",
  476                        *full_name);
  477          ap_lingering_close(r->connection);
  478      exit(0);
  479     }
  480  
  481     return status;
  482 }
  483 
  484 static void itk_hooks(apr_pool_t *p)
  485 {
  486     /* add our version component on init, and check that we are under prefork */
  487     ap_hook_post_config(itk_init_handler, NULL, NULL, APR_HOOK_MIDDLE);
  488 
  489     /* we need to hook into the privilege dropping both before and after mod_unixd */
  490     ap_hook_drop_privileges(itk_pre_drop_privileges, NULL, NULL, APR_HOOK_FIRST);
  491     ap_hook_drop_privileges(itk_post_drop_privileges, NULL, NULL, APR_HOOK_LAST);
  492 
  493     /* fork just before ap_run_create_connection() is called */
  494     ap_hook_process_connection(itk_fork_process, NULL, NULL, APR_HOOK_REALLY_FIRST);
  495 
  496     /* set the uid as fast as possible, but not before merging per-dir config */
  497     ap_hook_post_perdir_config(itk_post_perdir_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
  498 
  499     /* replace core_dirwalk_stat so that we can kill the connection on stat() failure */
  500     ap_hook_dirwalk_stat(itk_dirwalk_stat, NULL, NULL, APR_HOOK_MIDDLE);
  501 
  502     /* hook htaccess check so we can kill the connection on .htaccess open() failure */
  503     ap_hook_open_htaccess(itk_open_htaccess, NULL, NULL, APR_HOOK_REALLY_FIRST); 
  504 }
  505 
  506 static const char *assign_user_id (cmd_parms *cmd, void *ptr, const char *user_name, const char *group_name)
  507 {
  508     itk_per_dir_conf *dconf = (itk_per_dir_conf *) ptr;
  509 
  510     const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
  511     if (err) {
  512         return err;
  513     }
  514 
  515     dconf->username = apr_pstrdup(cmd->pool, user_name);
  516     dconf->uid = ap_uname2id(user_name);
  517     dconf->gid = ap_gname2id(group_name);
  518     return NULL;
  519 }
  520 
  521 static const char *limit_uid_range(cmd_parms *cmd, void *dummy, const char *min_arg, const char *max_arg)
  522 {
  523     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
  524     if (err != NULL) {
  525         return err;
  526     }
  527 
  528     ap_itk_min_uid = atoi(min_arg);
  529     ap_itk_max_uid = atoi(max_arg);
  530     return NULL;
  531 }
  532 
  533 static const char *limit_gid_range(cmd_parms *cmd, void *dummy, const char *min_arg, const char *max_arg)
  534 {
  535     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
  536     if (err != NULL) {
  537         return err;
  538     }
  539 
  540     ap_itk_min_gid = atoi(min_arg);
  541     ap_itk_max_gid = atoi(max_arg);
  542     return NULL;
  543 }
  544 
  545 
  546 #if HAVE_LIBCAP
  547 static const char *enable_caps(cmd_parms *cmd, void *dummy, int arg)
  548 {
  549     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
  550     if (err != NULL) {
  551         return err;
  552     }
  553     ap_itk_enable_caps = arg;
  554     return NULL;
  555 }
  556 #endif
  557 
  558  
  559 static const char *assign_user_id_expr (cmd_parms *cmd, void *ptr, const char *user_name_expr)
  560 {
  561     itk_per_dir_conf *dconf = (itk_per_dir_conf *) ptr;
  562 
  563     const char *err;
  564 
  565     err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
  566     if (err) {
  567         return err;
  568     }
  569 
  570     dconf->uid_expr = ap_expr_parse_cmd_mi(cmd,
  571                                            user_name_expr,
  572                                            AP_EXPR_FLAG_STRING_RESULT,
  573                                            &err,
  574                                            NULL,
  575                                            AP_CORE_MODULE_INDEX);
  576     if (err) {
  577         return err;
  578     }
  579 
  580     return NULL;
  581 }
  582 
  583 static const char *assign_group_id_expr (cmd_parms *cmd, void *ptr, const char *group_name_expr)
  584 {
  585     itk_per_dir_conf *dconf = (itk_per_dir_conf *) ptr;
  586 
  587     const char *err;
  588 
  589     err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
  590     if (err) {
  591         return err;
  592     }
  593 
  594     dconf->gid_expr = ap_expr_parse_cmd_mi(cmd,
  595                                            group_name_expr,
  596                                            AP_EXPR_FLAG_STRING_RESULT,
  597                                            &err,
  598                                            NULL,
  599                                            AP_CORE_MODULE_INDEX);
  600     if (err) {
  601         return err;
  602     }
  603     return NULL;
  604 }
  605 
  606 static const char *set_max_clients_vhost (cmd_parms *cmd, void *dummy, const char *arg)
  607 {
  608     itk_server_conf *sconf =
  609         (itk_server_conf *) ap_get_module_config(cmd->server->module_config, &mpm_itk_module);
  610     sconf->max_clients_vhost = atoi(arg);
  611     return NULL;
  612 }
  613 
  614 static const char *set_nice_value (cmd_parms *cmd, void *ptr, const char *arg)
  615 {
  616     itk_per_dir_conf *dconf = (itk_per_dir_conf *) ptr;
  617     int nice_value = atoi(arg);
  618 
  619     if (nice_value < -20) {
  620         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
  621                      "WARNING: NiceValue of %d is below -20, increasing NiceValue to -20.",
  622                      nice_value);
  623         nice_value = -20;
  624     }
  625     else if (nice_value > 19) {
  626         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
  627                      "WARNING: NiceValue of %d is above 19, lowering NiceValue to 19.",
  628                      nice_value);
  629         nice_value = 19;
  630     }
  631     dconf->nice_value = nice_value;
  632     return NULL;
  633 }
  634 
  635 static const command_rec itk_cmds[] = {
  636 AP_INIT_TAKE2("AssignUserID", assign_user_id, NULL, RSRC_CONF|ACCESS_CONF,
  637               "Tie a virtual host to a specific child process."),
  638 AP_INIT_RAW_ARGS("AssignUserIDExpr", assign_user_id_expr, NULL, RSRC_CONF|ACCESS_CONF,
  639                  "Choose user ID given an expression. Will override AssignUserID."),
  640 AP_INIT_RAW_ARGS("AssignGroupIDExpr", assign_group_id_expr, NULL, RSRC_CONF|ACCESS_CONF,
  641                  "Choose group ID given an expression. Will override AssignUserID."),
  642 AP_INIT_TAKE2("LimitUIDRange", limit_uid_range, NULL, RSRC_CONF,
  643               "If seccomp v2 is available (Linux 3.5.0+), limit the process's possible "
  644               "uid to the given range (inclusive endpoints)"),
  645 AP_INIT_TAKE2("LimitGIDRange", limit_gid_range, NULL, RSRC_CONF,
  646               "If seccomp v2 is available (Linux 3.5.0+), limit the process's possible "
  647               "primary gid to the given range (inclusive endpoints). "
  648               "Note that this does not restrict supplemental gids!"),
  649 #if HAVE_LIBCAP
  650 AP_INIT_FLAG("EnableCapabilities", enable_caps, NULL, RSRC_CONF,
  651              "Drop most root capabilities in the parent process, and instead run as "
  652              "the user given by the User/Group directives with some extra capabilities "
  653              "(in particular setuid). Somewhat more secure, but can cause problems "
  654              "when serving from NFS."),
  655 #endif
  656 AP_INIT_TAKE1("MaxClientsVHost", set_max_clients_vhost, NULL, RSRC_CONF,
  657               "Maximum number of children alive at the same time for this virtual host."),
  658 AP_INIT_TAKE1("NiceValue", set_nice_value, NULL, RSRC_CONF|ACCESS_CONF,
  659               "Set nice value for the given vhost, from -20 (highest priority) to 19 (lowest priority)."),
  660 { NULL }
  661 };
  662 
  663 /* == allocate a private per-dir config structure == */
  664 static void *itk_create_dir_config(apr_pool_t *p, char *dummy)
  665 {
  666     itk_per_dir_conf *c = (itk_per_dir_conf *)
  667         apr_pcalloc(p, sizeof(itk_per_dir_conf));
  668     c->uid = c->gid = -1;
  669     c->uid_expr = c->gid_expr = NULL;
  670     c->nice_value = UNSET_NICE_VALUE;
  671     return c;
  672 }
  673 
  674 /* == merge the parent per-dir config structure into ours == */
  675 static void *itk_merge_dir_config(apr_pool_t *p, void *parent_ptr, void *child_ptr)
  676 {
  677     itk_per_dir_conf *c = (itk_per_dir_conf *)
  678         itk_create_dir_config(p, NULL);
  679     itk_per_dir_conf *parent = (itk_per_dir_conf *) parent_ptr;
  680     itk_per_dir_conf *child = (itk_per_dir_conf *) child_ptr;
  681 
  682     if (child->username != NULL) {
  683       c->username = child->username;
  684       c->uid = child->uid;
  685       c->gid = child->gid;
  686     } else {
  687       c->username = parent->username;
  688       c->uid = parent->uid;
  689       c->gid = parent->gid;
  690     }
  691     if (child->uid_expr != NULL) {
  692       c->uid_expr = child->uid_expr;
  693     } else {
  694       c->uid_expr = parent->uid_expr;
  695     }
  696     if (child->gid_expr != NULL) {
  697       c->gid_expr = child->gid_expr;
  698     } else {
  699       c->gid_expr = parent->gid_expr;
  700     }
  701     if (child->nice_value != UNSET_NICE_VALUE) {
  702       c->nice_value = child->nice_value;
  703     } else {
  704       c->nice_value = parent->nice_value;
  705     }
  706     return c;
  707 }
  708 
  709 /* == allocate a private server config structure == */
  710 static void *itk_create_server_config(apr_pool_t *p, server_rec *s)
  711 {
  712     itk_server_conf *c = (itk_server_conf *)
  713         apr_pcalloc(p, sizeof(itk_server_conf));
  714     c->max_clients_vhost = -1;
  715     return c;
  716 }
  717 
  718 AP_DECLARE_MODULE(mpm_itk) = {
  719     STANDARD20_MODULE_STUFF,
  720     itk_create_dir_config,      /* create per-directory config structure */
  721     itk_merge_dir_config,       /* merge per-directory config structures */
  722     itk_create_server_config,   /* create per-server config structure */
  723     NULL,                       /* merge per-server config structures */
  724     itk_cmds,                   /* command apr_table_t */
  725     itk_hooks,                  /* register hooks */
  726 };