Pam

vsftpd/pam_userdb.so - 自動創建虛擬使用者主目錄

  • May 16, 2018

設置:

  • 亞馬遜 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;
}

引用自:https://serverfault.com/questions/911451