Blame modules/arch/unix/mod_unixd.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include "ap_config.h"
Packit 90a5c9
#include "httpd.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "http_main.h"
Packit 90a5c9
#include "http_log.h"
Packit 90a5c9
#include "http_core.h"
Packit 90a5c9
#include "mpm_common.h"
Packit 90a5c9
#include "os.h"
Packit 90a5c9
#include "ap_mpm.h"
Packit 90a5c9
#include "mod_unixd.h"
Packit 90a5c9
#include "apr_thread_proc.h"
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "apr_portable.h"
Packit 90a5c9
#ifdef HAVE_PWD_H
Packit 90a5c9
#include <pwd.h>
Packit 90a5c9
#endif
Packit 90a5c9
#ifdef HAVE_SYS_RESOURCE_H
Packit 90a5c9
#include <sys/resource.h>
Packit 90a5c9
#endif
Packit 90a5c9
/* XXX */
Packit 90a5c9
#include <sys/stat.h>
Packit 90a5c9
#ifdef HAVE_UNISTD_H
Packit 90a5c9
#include <unistd.h>
Packit 90a5c9
#endif
Packit 90a5c9
#ifdef HAVE_GRP_H
Packit 90a5c9
#include <grp.h>
Packit 90a5c9
#endif
Packit 90a5c9
#ifdef HAVE_STRINGS_H
Packit 90a5c9
#include <strings.h>
Packit 90a5c9
#endif
Packit 90a5c9
#ifdef HAVE_SYS_SEM_H
Packit 90a5c9
#include <sys/sem.h>
Packit 90a5c9
#endif
Packit 90a5c9
#ifdef HAVE_SYS_PRCTL_H
Packit 90a5c9
#include <sys/prctl.h>
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
#ifndef DEFAULT_USER
Packit 90a5c9
#define DEFAULT_USER "#-1"
Packit 90a5c9
#endif
Packit 90a5c9
#ifndef DEFAULT_GROUP
Packit 90a5c9
#define DEFAULT_GROUP "#-1"
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
#if 0
Packit 90a5c9
typedef struct {
Packit 90a5c9
  const char *user_name;
Packit 90a5c9
  uid_t user_id;
Packit 90a5c9
  gid_t group_id;
Packit 90a5c9
  const char *chroot_dir;
Packit 90a5c9
} unixd_config_t;
Packit 90a5c9
#else
Packit 90a5c9
/*
Packit 90a5c9
 * TODO: clean up the separation between this code
Packit 90a5c9
 *       and its data structures and unixd.c, as shown
Packit 90a5c9
 *       by the fact that we include unixd.h. Create
Packit 90a5c9
 *       mod_unixd.h which does what we need and
Packit 90a5c9
 *       clean up unixd.h for what it no longer needs
Packit 90a5c9
 */
Packit 90a5c9
#include "unixd.h"
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/* Set group privileges.
Packit 90a5c9
 *
Packit 90a5c9
 * Note that we use the username as set in the config files, rather than
Packit 90a5c9
 * the lookup of to uid --- the same uid may have multiple passwd entries,
Packit 90a5c9
 * with different sets of groups for each.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
static int set_group_privs(void)
Packit 90a5c9
{
Packit 90a5c9
    if (!geteuid()) {
Packit 90a5c9
        const char *name;
Packit 90a5c9
Packit 90a5c9
        /* Get username if passed as a uid */
Packit 90a5c9
Packit 90a5c9
        if (ap_unixd_config.user_name[0] == '#') {
Packit 90a5c9
            struct passwd *ent;
Packit 90a5c9
            uid_t uid = atol(&ap_unixd_config.user_name[1]);
Packit 90a5c9
Packit 90a5c9
            if ((ent = getpwuid(uid)) == NULL) {
Packit 90a5c9
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02155)
Packit 90a5c9
                         "getpwuid: couldn't determine user name from uid %ld, "
Packit 90a5c9
                         "you probably need to modify the User directive",
Packit 90a5c9
                         (long)uid);
Packit 90a5c9
                return -1;
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            name = ent->pw_name;
Packit 90a5c9
        }
Packit 90a5c9
        else
Packit 90a5c9
            name = ap_unixd_config.user_name;
Packit 90a5c9
Packit 90a5c9
#if !defined(OS2)
Packit 90a5c9
        /* OS/2 doesn't support groups. */
Packit 90a5c9
        /*
Packit 90a5c9
         * Set the GID before initgroups(), since on some platforms
Packit 90a5c9
         * setgid() is known to zap the group list.
Packit 90a5c9
         */
Packit 90a5c9
        if (setgid(ap_unixd_config.group_id) == -1) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02156)
Packit 90a5c9
                        "setgid: unable to set group id to Group %ld",
Packit 90a5c9
                        (long)ap_unixd_config.group_id);
Packit 90a5c9
            return -1;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        /* Reset `groups' attributes. */
Packit 90a5c9
Packit 90a5c9
        if (initgroups(name, ap_unixd_config.group_id) == -1) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02157)
Packit 90a5c9
                        "initgroups: unable to set groups for User %s "
Packit 90a5c9
                        "and Group %ld", name, (long)ap_unixd_config.group_id);
Packit 90a5c9
            return -1;
Packit 90a5c9
        }
Packit 90a5c9
#endif /* !defined(OS2) */
Packit 90a5c9
    }
Packit 90a5c9
    return 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static int
Packit 90a5c9
unixd_drop_privileges(apr_pool_t *pool, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    int rv = set_group_privs();
Packit 90a5c9
Packit 90a5c9
    if (rv) {
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (NULL != ap_unixd_config.chroot_dir) {
Packit 90a5c9
        if (geteuid()) {
Packit 90a5c9
            rv = errno;
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02158)
Packit 90a5c9
                         "Cannot chroot when not started as root");
Packit 90a5c9
            return rv;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (chdir(ap_unixd_config.chroot_dir) != 0) {
Packit 90a5c9
            rv = errno;
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02159)
Packit 90a5c9
                         "Can't chdir to %s", ap_unixd_config.chroot_dir);
Packit 90a5c9
            return rv;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (chroot(ap_unixd_config.chroot_dir) != 0) {
Packit 90a5c9
            rv = errno;
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02160)
Packit 90a5c9
                         "Can't chroot to %s", ap_unixd_config.chroot_dir);
Packit 90a5c9
            return rv;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (chdir("/") != 0) {
Packit 90a5c9
            rv = errno;
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02161)
Packit 90a5c9
                         "Can't chdir to new root");
Packit 90a5c9
            return rv;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Only try to switch if we're running as root */
Packit 90a5c9
    if (!geteuid() && (
Packit 90a5c9
#ifdef _OSD_POSIX
Packit 90a5c9
        os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
Packit 90a5c9
#endif
Packit 90a5c9
        setuid(ap_unixd_config.user_id) == -1)) {
Packit 90a5c9
        rv = errno;
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02162)
Packit 90a5c9
                    "setuid: unable to change to uid: %ld",
Packit 90a5c9
                    (long) ap_unixd_config.user_id);
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
Packit 90a5c9
    /* this applies to Linux 2.4+ */
Packit 90a5c9
    if (ap_coredumpdir_configured) {
Packit 90a5c9
        if (prctl(PR_SET_DUMPABLE, 1)) {
Packit 90a5c9
            rv = errno;
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02163)
Packit 90a5c9
                         "set dumpable failed - this child will not coredump"
Packit 90a5c9
                         " after software errors");
Packit 90a5c9
            return rv;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static const char *
Packit 90a5c9
unixd_set_user(cmd_parms *cmd, void *dummy,
Packit 90a5c9
               const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
Packit 90a5c9
    if (err != NULL) {
Packit 90a5c9
        return err;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ap_unixd_config.user_name = arg;
Packit 90a5c9
    ap_unixd_config.user_id = ap_uname2id(arg);
Packit 90a5c9
#if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
Packit 90a5c9
    if (ap_unixd_config.user_id == 0) {
Packit 90a5c9
        return "Error:\tApache has not been designed to serve pages while\n"
Packit 90a5c9
                "\trunning as root.  There are known race conditions that\n"
Packit 90a5c9
                "\twill allow any local user to read any file on the system.\n"
Packit 90a5c9
                "\tIf you still desire to serve pages as root then\n"
Packit 90a5c9
                "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
Packit 90a5c9
                "\tand then rebuild the server.\n"
Packit 90a5c9
                "\tIt is strongly suggested that you instead modify the User\n"
Packit 90a5c9
                "\tdirective in your httpd.conf file to list a non-root\n"
Packit 90a5c9
                "\tuser.\n";
Packit 90a5c9
    }
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char*
Packit 90a5c9
unixd_set_group(cmd_parms *cmd, void *dummy,
Packit 90a5c9
                                         const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
Packit 90a5c9
    if (err != NULL) {
Packit 90a5c9
        return err;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ap_unixd_config.group_name = arg;
Packit 90a5c9
    ap_unixd_config.group_id = ap_gname2id(arg);
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char*
Packit 90a5c9
unixd_set_chroot_dir(cmd_parms *cmd, void *dummy,
Packit 90a5c9
                    const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
Packit 90a5c9
    if (err != NULL) {
Packit 90a5c9
        return err;
Packit 90a5c9
    }
Packit 90a5c9
    if (!ap_is_directory(cmd->pool, arg)) {
Packit 90a5c9
        return "ChrootDir must be a valid directory";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ap_unixd_config.chroot_dir = arg;
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *
Packit 90a5c9
unixd_set_suexec(cmd_parms *cmd, void *dummy, int arg)
Packit 90a5c9
{
Packit 90a5c9
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
Packit 90a5c9
Packit 90a5c9
    if (err != NULL) {
Packit 90a5c9
        return err;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!ap_unixd_config.suexec_enabled && arg) {
Packit 90a5c9
        return apr_pstrcat(cmd->pool, "suEXEC isn't supported: ",
Packit 90a5c9
                           ap_unixd_config.suexec_disabled_reason, NULL);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!arg) {
Packit 90a5c9
        ap_unixd_config.suexec_disabled_reason = "Suexec directive is Off";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ap_unixd_config.suexec_enabled = arg;
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
#ifdef AP_SUEXEC_CAPABILITIES
Packit 90a5c9
/* If suexec is using capabilities, don't test for the setuid bit. */
Packit 90a5c9
#define SETUID_TEST(finfo) (1)
Packit 90a5c9
#else
Packit 90a5c9
#define SETUID_TEST(finfo) (finfo.protection & APR_USETID)
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
static int
Packit 90a5c9
unixd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
Packit 90a5c9
                 apr_pool_t *ptemp)
Packit 90a5c9
{
Packit 90a5c9
    apr_finfo_t wrapper;
Packit 90a5c9
    ap_unixd_config.user_name = DEFAULT_USER;
Packit 90a5c9
    ap_unixd_config.user_id = ap_uname2id(DEFAULT_USER);
Packit 90a5c9
    ap_unixd_config.group_name = DEFAULT_GROUP;
Packit 90a5c9
    ap_unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
Packit 90a5c9
Packit 90a5c9
    ap_unixd_config.chroot_dir = NULL; /* none */
Packit 90a5c9
Packit 90a5c9
    /* Check for suexec */
Packit 90a5c9
    ap_unixd_config.suexec_enabled = 0;
Packit 90a5c9
    if ((apr_stat(&wrapper, SUEXEC_BIN, APR_FINFO_NORM, ptemp))
Packit 90a5c9
         == APR_SUCCESS) {
Packit 90a5c9
        if (SETUID_TEST(wrapper) && wrapper.user == 0
Packit 90a5c9
            && (access(SUEXEC_BIN, R_OK|X_OK) == 0)) {
Packit 90a5c9
            ap_unixd_config.suexec_enabled = 1;
Packit 90a5c9
            ap_unixd_config.suexec_disabled_reason = "";
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            ap_unixd_config.suexec_disabled_reason =
Packit 90a5c9
                "Invalid owner or file mode for " SUEXEC_BIN;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        ap_unixd_config.suexec_disabled_reason =
Packit 90a5c9
            "Missing suexec binary " SUEXEC_BIN;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ap_sys_privileges_handlers(1);
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE(int) ap_unixd_setup_child(void)
Packit 90a5c9
{
Packit 90a5c9
    if (set_group_privs()) {
Packit 90a5c9
        return -1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (NULL != ap_unixd_config.chroot_dir) {
Packit 90a5c9
        if (geteuid()) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02164)
Packit 90a5c9
                         "Cannot chroot when not started as root");
Packit 90a5c9
            return -1;
Packit 90a5c9
        }
Packit 90a5c9
        if (chdir(ap_unixd_config.chroot_dir) != 0) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02165)
Packit 90a5c9
                         "Can't chdir to %s", ap_unixd_config.chroot_dir);
Packit 90a5c9
            return -1;
Packit 90a5c9
        }
Packit 90a5c9
        if (chroot(ap_unixd_config.chroot_dir) != 0) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02166)
Packit 90a5c9
                         "Can't chroot to %s", ap_unixd_config.chroot_dir);
Packit 90a5c9
            return -1;
Packit 90a5c9
        }
Packit 90a5c9
        if (chdir("/") != 0) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02167)
Packit 90a5c9
                         "Can't chdir to new root");
Packit 90a5c9
            return -1;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Only try to switch if we're running as root */
Packit 90a5c9
    if (!geteuid() && (
Packit 90a5c9
#ifdef _OSD_POSIX
Packit 90a5c9
        os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
Packit 90a5c9
#endif
Packit 90a5c9
        setuid(ap_unixd_config.user_id) == -1)) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02168)
Packit 90a5c9
                    "setuid: unable to change to uid: %ld",
Packit 90a5c9
                    (long) ap_unixd_config.user_id);
Packit 90a5c9
        return -1;
Packit 90a5c9
    }
Packit 90a5c9
#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
Packit 90a5c9
    /* this applies to Linux 2.4+ */
Packit 90a5c9
    if (ap_coredumpdir_configured) {
Packit 90a5c9
        if (prctl(PR_SET_DUMPABLE, 1)) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02169)
Packit 90a5c9
                         "set dumpable failed - this child will not coredump"
Packit 90a5c9
                         " after software errors");
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
#endif
Packit 90a5c9
    return 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void unixd_dump_config(apr_pool_t *p, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    apr_file_t *out = NULL;
Packit 90a5c9
    apr_uid_t uid = ap_unixd_config.user_id;
Packit 90a5c9
    apr_gid_t gid = ap_unixd_config.group_id;
Packit 90a5c9
    char *no_root = "";
Packit 90a5c9
    if (!ap_exists_config_define("DUMP_RUN_CFG"))
Packit 90a5c9
        return;
Packit 90a5c9
    if (geteuid() != 0)
Packit 90a5c9
        no_root = " not_used";
Packit 90a5c9
    apr_file_open_stdout(&out, p);
Packit 90a5c9
    apr_file_printf(out, "User: name=\"%s\" id=%lu%s\n",
Packit 90a5c9
                    ap_unixd_config.user_name, (unsigned long)uid, no_root);
Packit 90a5c9
    apr_file_printf(out, "Group: name=\"%s\" id=%lu%s\n",
Packit 90a5c9
                    ap_unixd_config.group_name, (unsigned long)gid, no_root);
Packit 90a5c9
    if (ap_unixd_config.chroot_dir)
Packit 90a5c9
        apr_file_printf(out, "ChrootDir: \"%s\"%s\n",
Packit 90a5c9
                        ap_unixd_config.chroot_dir, no_root);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void unixd_hooks(apr_pool_t *pool)
Packit 90a5c9
{
Packit 90a5c9
    ap_hook_pre_config(unixd_pre_config,
Packit 90a5c9
                       NULL, NULL, APR_HOOK_FIRST);
Packit 90a5c9
    ap_hook_test_config(unixd_dump_config,
Packit 90a5c9
                        NULL, NULL, APR_HOOK_FIRST);
Packit 90a5c9
    ap_hook_drop_privileges(unixd_drop_privileges,
Packit 90a5c9
                            NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const command_rec unixd_cmds[] = {
Packit 90a5c9
    AP_INIT_TAKE1("User", unixd_set_user, NULL, RSRC_CONF,
Packit 90a5c9
                  "Effective user id for this server"),
Packit 90a5c9
    AP_INIT_TAKE1("Group", unixd_set_group, NULL, RSRC_CONF,
Packit 90a5c9
                  "Effective group id for this server"),
Packit 90a5c9
    AP_INIT_TAKE1("ChrootDir", unixd_set_chroot_dir, NULL, RSRC_CONF,
Packit 90a5c9
                  "The directory to chroot(2) into"),
Packit 90a5c9
    AP_INIT_FLAG("Suexec", unixd_set_suexec, NULL, RSRC_CONF,
Packit 90a5c9
                 "Enable or disable suEXEC support"),
Packit 90a5c9
    {NULL}
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(unixd) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    NULL,
Packit 90a5c9
    NULL,
Packit 90a5c9
    NULL,
Packit 90a5c9
    NULL,
Packit 90a5c9
    unixd_cmds,
Packit 90a5c9
    unixd_hooks
Packit 90a5c9
};
Packit 90a5c9