Pam
vsftpd/pam_userdb.so - 自動創建虛擬使用者主目錄
設置:
- 亞馬遜 Linux EC2
- vsftpd
- 使用 pam_userdb.so 進行 PAM 身份驗證
- 從外部源 (lsyncd) 寫入伯克利使用者 db 的使用者名/密碼。
有成千上萬的虛擬使用者,到目前為止,我已經在 /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.so
- /etc/nsswitch.conf
但我似乎無法將所有這些放在一起來為 Berkeley DB 解決這個問題。
我最終為 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 的身份驗證和帳戶更改為“必要”,以避免在 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; }