Назад | Перейти на главную страницу

Vsftpd / pam_userdb.so - автоматическое создание домашних каталогов виртуальных пользователей

Настроить:

Есть много тысяч виртуальных пользователей, на сегодняшний день я вручную предварительно создал для них домашние каталоги в / home / vsftpd.

drwx------    2 vsftpd users     4096 Apr 11 15:28 user0123
drwx------    2 vsftpd users     4096 Apr 11 15:28 user0124
...

#%PAM-1.0
auth required pam_userdb.so db=/usr/local/vsftpd_auth/vsftpd_userdb crypt=none
account required pam_userdb.so db=/usr/local/vsftpd_auth/vsftpd_userdb

Я хочу избежать ручного предварительного создания каталогов, чтобы новые записи в Berkeley DB работали автоматически без изменения каждого узла.

Поиск дает аналогичные вопросы для аутентификации LDAP и MySQL с использованием:

Но я не могу собрать все вместе, чтобы решить эту проблему для Berkeley DB.

В итоге я реализовал модуль PAM для виртуальных пользователей vsftpd, в значительной степени основанный на pam_mkhomedir.so. Я уверен, что его можно улучшить, но ниже представлена ​​рабочая версия.

Использование:

pam_mkhomedir_vsftpd_virt.so [debug] vsftpd_user=<vsftpd_user> basedir=<basedir> 
  • vsftpd_user - обычно vsftpd
  • basedir - обычно / home / vsftpd /

/etc/pam.d/vsftpd:

#%PAM-1.0
auth requisite pam_userdb.so db=/path/to/userdb crypt=none
account requisite pam_userdb.so db=/path/to/userdb
account required pam_mkhomedir_vsftpd_virt.so debug vsftpd_user=vsftpd basedir=/home/vsftpd/
  • Я изменил аутентификацию и учетную запись для pam_userdb.so на «Requisite», чтобы избежать создания домашнего каталога, если аутентификация userdb не проходит.
  • Я реализовал модуль для работы на уровне учетной записи, потому что сеансы не используются в моем контексте vsftpd.

Компиляция:

gcc -fPIC -c pam_mkhomedir_vsftpd_virt.c
gcc -shared -o pam_mkhomedir_vsftpd_virt.so pam_mkhomedir_vsftpd_virt.o -lpam
  • Установите pam_mkhomedir_vsftpd_virt.so с другими модулями PAM.

Код:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <unistd.h>
#include <syslog.h>

/* For now we will use the service function for account management
 */
#define PAM_SM_ACCOUNT
#include <security/pam_modules.h>

#define MAX_HOMEDIR_SIZE 100

typedef struct {
    bool debug;
    const char *vsftpd_user;
    const char *basedir;
    const char *user;
    char homedir[MAX_HOMEDIR_SIZE+1];
} options_t;

static int parse_input(pam_handle_t *pamh, int flags, int argc, const char **argv, options_t *options) {
    int rc;
    int basedir_len;
    int total_len;
    bool add_slash = false;
    int i;

    /* Retrieve the user name
     */
    rc = pam_get_item(pamh, PAM_USER, (void *)&options->user);

    if (rc != PAM_SUCCESS || options->user == NULL || *(options->user) == '\0') {
        pam_syslog(pamh, LOG_ERR, "cannot retrieve the user name");
        return PAM_USER_UNKNOWN;
    }

    /* Retrieve the module parms
     */
    for (i = 0 ; i < argc; *argv++, i++) {
        if (strcmp(*argv, "debug") == 0) {
            options->debug = true;
        }
        else if (strncmp(*argv, "vsftpd_user=", 12) == 0) {
            options->vsftpd_user = *argv+12;
        }
        else if (strncmp(*argv, "basedir=", 8) == 0) {
            options->basedir = *argv+8;
        }
        else {
            pam_syslog(pamh, LOG_ERR, "unknown option '%s'", *argv);
        }
    }

    /* Validate input
     */
    if (options->vsftpd_user == NULL || *(options->vsftpd_user) == '\0') {
        pam_syslog(pamh, LOG_ERR, "cannot retrieve the vsftpd user");
        return PAM_NO_MODULE_DATA;
    }

    if (options->basedir == NULL || *(options->basedir) == '\0') {
        pam_syslog(pamh, LOG_ERR, "cannot retrieve the base dir");
        return PAM_NO_MODULE_DATA;
    }

    if (options->basedir[0] != '/') {
        pam_syslog(pamh, LOG_ERR, "base dir must start with '/'");
        return PAM_NO_MODULE_DATA;
    }

    /* Check whether we need to add a slash to the path
     */
    basedir_len = (int) strlen(options->basedir);

    if (options->basedir[basedir_len-1] != '/')
        add_slash = true;

    /* Verify we haven't exceeded the max dir length
     */
    total_len = basedir_len + (int) strlen(options->user) + (add_slash?1:0);

    if (total_len > MAX_HOMEDIR_SIZE) {
        pam_syslog(pamh, LOG_ERR, "home directory max length of %d exceeded '%d'", MAX_HOMEDIR_SIZE, total_len);
        return PAM_BUF_ERR;
    }

    /* Create the homedir string
     */
    snprintf(options->homedir, MAX_HOMEDIR_SIZE+1, "%s%s%s", options->basedir, add_slash?"/":"", options->user);

    /* Finished parsing input, log what we got...
     */
    if (options->debug) {
        pam_syslog(pamh, LOG_DEBUG, "vsftpd user '%s'", options->vsftpd_user);
        pam_syslog(pamh, LOG_DEBUG, "base directory '%s'", options->basedir);
        pam_syslog(pamh, LOG_DEBUG, "user '%s'", options->user);
        pam_syslog(pamh, LOG_DEBUG, "home directory '%s'", options->homedir);
    }

    return PAM_SUCCESS;
}

static int create_homedir(pam_handle_t *pamh, options_t *options) {
    struct stat status;
    struct passwd *pwd;
    const char *vsftpd_user = options->vsftpd_user;
    char *homedir = options->homedir;

    /* Retrieve passwd data for the vsftpd user
     */
    pwd = getpwnam(vsftpd_user);

    if (pwd == NULL) {
        pam_syslog(pamh, LOG_ERR, "unable to get user creds for '%s'", vsftpd_user);
        return PAM_CRED_INSUFFICIENT;
    }

    /* Check if home directory already exists
     */
    if (stat(homedir, &status) == 0) {
        if (options->debug)
            pam_syslog(pamh, LOG_DEBUG, "home directory '%s' already exists", homedir);
        return PAM_SUCCESS;
    }

    /* Home directory doesn't exist, create it
     */
    if (options->debug)
        pam_syslog(pamh, LOG_DEBUG, "creating home directory '%s'", homedir);

    if (mkdir(homedir, 0700) != 0) {
        pam_syslog(pamh, LOG_ERR, "unable to create home directory '%s'", homedir);
        return PAM_PERM_DENIED;
    }

    if (chmod(homedir, 0700) != 0 || chown(homedir, pwd->pw_uid, pwd->pw_gid) != 0) {
        pam_syslog(pamh, LOG_ERR, "unable to change perms on directory '%s'", homedir);
        return PAM_PERM_DENIED;
    }

    return PAM_SUCCESS;
}

/* PAM Account Management function
 */
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    options_t options;
    int rc;

    memset(&options, 0, sizeof(options_t));

    rc = parse_input(pamh, flags, argc, argv, &options);

    if (rc != PAM_SUCCESS) {
        return rc;
    }

    rc = create_homedir(pamh, &options);

    if (rc != PAM_SUCCESS) {
        return rc;
    }

    return PAM_SUCCESS;
}