home directories.
--HG--
branch : HEAD
the user name. This behaviour works with all authentication mechanisms and
databases.
+Home directory can be prefixed with "<chroot>/./" in which case <chroot>
+directory will be chrooted into. The actual home directory follows the
+"/./". For example "/chroot/./home/user".
+
passwd
------
exception to this, they still set the password field even with shadow
passwords.
-As an extension, if home directory ends with "/./", it will be chrooted to.
shadow
------
# System user name (for initgroups())
# System UID
# System GID
-# Chroot to home directory? (yes / no)
#user_attrs = uid,homeDirectory,,uid,uidNumber,gidNumber
# Filter for user lookup. Some variables can be used:
# system_user - System user name (for initgroups())
# uid - System UID
# gid - System GID
-# chroot - Chroot to home directory? (Y / N)
#
# Either home or mail is required. uid and gid are required. If more than one
# row is returned or there's missing fields, login will automatically fail.
unsigned int tag;
unsigned int success:1;
- unsigned int chroot:1; /* chroot to home directory */
uid_t uid;
gid_t gid;
Ignore if it points outside data_size. */
size_t system_user_idx;
size_t virtual_user_idx;
- size_t home_idx, mail_idx;
+ size_t home_idx, mail_idx, chroot_idx;
size_t data_size;
/* unsigned char data[]; */
/* flags */
if (*args != NULL) {
- if (strstr(*args, "chroot") != NULL)
- pu->chroot = TRUE;
+ /* no flags currently */
args++;
}
char *mail;
char *password;
-
- unsigned int chroot:1;
};
struct passwd_file {
{
struct auth_master_reply *reply;
buffer_t *buf;
+ char *p;
buf = buffer_create_dynamic(data_stack_pool,
sizeof(*reply) + 256, (size_t)-1);
reply->success = TRUE;
- reply->chroot = user->chroot;
reply->uid = user->uid;
reply->gid = user->gid;
reply->system_user_idx = reply_add(buf, user->system_user);
reply->virtual_user_idx = reply_add(buf, user->virtual_user);
- reply->home_idx = reply_add(buf, user->home);
reply->mail_idx = reply_add(buf, user->mail);
+ p = strstr(user->home, "/./");
+ if (p == NULL) {
+ reply->home_idx = reply_add(buf, user->home);
+ reply->chroot_idx = reply_add(buf, NULL);
+ } else {
+ /* wu-ftpd like <chroot>/./<home> */
+ reply->home_idx = reply_add(buf, t_strdup_until(user->home, p));
+ reply->chroot_idx = reply_add(buf, p + 3);
+ }
+
*reply_size = buffer_get_used_size(buf);
reply->data_size = *reply_size - sizeof(*reply);
return reply;
ATTR_SYSTEM_USER,
ATTR_UID_NUMBER,
ATTR_GID_NUMBER,
- ATTR_CHROOT,
ATTR_COUNT
};
case ATTR_GID_NUMBER:
user->gid = atoi(value);
break;
- case ATTR_CHROOT:
- user->chroot = value[0] == 'Y' || value[0] == 'y';
- break;
case ATTR_COUNT:
break;
data.home = pu->home;
data.mail = pu->mail;
- data.chroot = pu->chroot;
-
callback(&data, context);
}
{
struct user_data data;
struct passwd *pw;
- size_t len;
pw = getpwnam(user);
if (pw == NULL) {
data.gid = pw->pw_gid;
data.virtual_user = data.system_user = pw->pw_name;
-
- len = strlen(pw->pw_dir);
- if (len < 3 || strcmp(pw->pw_dir + len - 3, "/./") != 0)
- data.home = pw->pw_dir;
- else {
- /* wu-ftpd uses <chroot>/./<dir>. We don't support
- the dir after chroot, but this should work well enough. */
- data.home = t_strndup(pw->pw_dir, len-3);
- data.chroot = TRUE;
- }
+ data.home = pw->pw_dir;
callback(&data, context);
}
struct userdb_pgsql_request *urequest =
(struct userdb_pgsql_request *) request;
struct user_data user;
- const char *str;
if (res != NULL && is_result_valid(res)) {
memset(&user, 0, sizeof(user));
user.mail = pg_get_str(res, "mail");
user.uid = atoi(PQgetvalue(res, 0, PQfnumber(res, "uid")));
user.gid = atoi(PQgetvalue(res, 0, PQfnumber(res, "gid")));
- str = pg_get_str(res, "chroot");
- user.chroot = str != NULL && (*str == 'Y' || *str == 'y');
urequest->userdb_callback(&user, request->context);
} else {
urequest->userdb_callback(NULL, request->context);
const char *system_user;
uid_t uid;
gid_t gid;
-
- int chroot; /* chroot to home directory */
};
typedef void userdb_callback_t(struct user_data *user, void *context);
reply->virtual_user_idx = nul_pos;
if (reply->home_idx >= reply->data_size)
reply->home_idx = nul_pos;
+ if (reply->chroot_idx >= reply->data_size)
+ reply->chroot_idx = nul_pos;
if (reply->mail_idx >= reply->data_size)
reply->mail_idx = nul_pos;
const char *data)
{
static const char *argv[] = { NULL, NULL, NULL };
- const char *host, *mail, *home_dir;
+ const char *host, *mail, *chroot_dir, *home_dir, *full_home_dir;
char title[1024];
pid_t pid;
int i, err;
if (!validate_uid_gid(reply->uid, reply->gid))
return FALSE;
- if (reply->chroot && !validate_chroot(data + reply->home_idx)) {
- i_error("Invalid chroot directory: %s", data + reply->home_idx);
+ home_dir = data + reply->home_idx;
+ chroot_dir = data + reply->chroot_idx;
+
+ if (*chroot_dir != '\0' && validate_chroot(chroot_dir)) {
+ i_error("Invalid chroot directory: %s", chroot_dir);
return FALSE;
}
/* setup environment - set the most important environment first
(paranoia about filling up environment without noticing) */
restrict_access_set_env(data + reply->system_user_idx,
- reply->uid, reply->gid,
- reply->chroot ? data + reply->home_idx : NULL);
+ reply->uid, reply->gid, chroot_dir);
restrict_process_size(process_size, (unsigned int)-1);
- home_dir = data + reply->home_idx;
if (*home_dir != '\0') {
- if (chdir(home_dir) < 0)
- i_fatal("chdir(%s) failed: %m", home_dir);
+ full_home_dir = *chroot_dir == '\0' ? home_dir :
+ t_strconcat(chroot_dir, "/", home_dir, NULL);
+ if (chdir(full_home_dir) < 0)
+ i_fatal("chdir(%s) failed: %m", full_home_dir);
}
env_put("LOGGED_IN=1");
if (*mail == '\0' && set->default_mail_env != NULL) {
mail = expand_mail_env(set->default_mail_env,
data + reply->virtual_user_idx,
- data + reply->home_idx);
+ home_dir);
}
env_put(t_strconcat("MAIL=", mail, NULL));