Vsftpd/pam_userdb.so-自动创建虚拟用户主目录

Vsftpd/pam_userdb.so-自动创建虚拟用户主目录

设置:

  • 亚马逊 Linux EC2
  • vsftpd
  • 使用 pam_userdb.so 进行 PAM 身份验证
  • 从外部来源(lsyncd)写入 Berkeley 用户数据库的用户名/密码。

有成千上万的虚拟用户,到目前为止,我已经在 /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 身份验证会产生类似的问题:

  • pam_mkhomedir 文件
  • /etc/nsswitch.conf

但我似乎无法将所有这些结合起来来解决 Berkeley DB 的这个问题。

答案1

我最终为 vsftpd 虚拟用户实现了一个 PAM 模块,大致基于 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 模块一起安装 pam_mkhomedir_vsftpd_virt.so。

代码:

#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;
}

相关内容