Acabei de implementar um módulo PAM para usuários virtuais do vsftpd, baseado em pam_mkhomedir.so. Tenho certeza que pode ser melhorado, mas abaixo está uma versão de trabalho.
Uso:
pam_mkhomedir_vsftpd_virt.so [debug] vsftpd_user=<vsftpd_user> basedir=<basedir>
- vsftpd_user - geralmente vsftpd
- basedir - normalmente / 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/
- Eu alterei a autenticação e a conta de pam_userdb.so para 'requisite' para evitar a criação do diretório home se a autenticação do userdb não passar.
- Eu implementei o módulo para atuar no nível da conta, porque as sessões não são usadas no meu contexto vsftpd.
Compilação:
gcc -fPIC -c pam_mkhomedir_vsftpd_virt.c
gcc -shared -o pam_mkhomedir_vsftpd_virt.so pam_mkhomedir_vsftpd_virt.o -lpam
- Instale pam_mkhomedir_vsftpd_virt.so com os outros módulos PAM.
Código:
#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) == 'pam_mkhomedir_vsftpd_virt.so [debug] vsftpd_user=<vsftpd_user> basedir=<basedir>
') {
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) == '#%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_syslog(pamh, LOG_ERR, "cannot retrieve the vsftpd user");
return PAM_NO_MODULE_DATA;
}
if (options->basedir == NULL || *(options->basedir) == 'gcc -fPIC -c pam_mkhomedir_vsftpd_virt.c
gcc -shared -o pam_mkhomedir_vsftpd_virt.so pam_mkhomedir_vsftpd_virt.o -lpam
') {
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;
}