Commit 89d93a75 89d93a75c4abec10b9cfe0041eaf654d00f0a0ee by Sergey Poznyakoff

Fix the use of erad-only maildir folders. Imap4d improvements.

New imap4d statement "retain-groups" instructs it to retain the
named supplementary user groups when switching to the user
privileges (previously, imap4d just switched to <user>:mail).
Read-only mailboxes are handled graciously: SELECT succeeds on
such mailboxes, bug marks them explicitly as read-only.

* imap4d/imap4d.c (user_retain_groups): New global.
New config statement "retain-groups".
(mu_get_user_groups): New function.
(imap4d_session_setup0): Use mu_switch_to_privs instead of
just setuid.
* imap4d/list.c (imap4d_list): Fix handling of
  LIST "" "/path/to/mbox"
* imap4d/select.c (imap4d_select0): Retry opening in read-only
mode if failed to open for read-write.
* libproto/maildir/mbox.c (maildir_mkfilename)
(mk_info_filename): Change return value and signature. All uses
changed.
(maildir_scan0): Don't shuffle messages if mailbox is opened
read-only.
* libproto/maildir/folder.c: Update call to maildir_mkfilename.
* libproto/maildir/maildir.h (maildir_mkfilename): Change
prototype.
1 parent 6074d4d8
......@@ -35,6 +35,8 @@ enum tls_mode tls_mode;
int login_disabled; /* Disable LOGIN command */
int create_home_dir; /* Create home directory if it does not
exist */
mu_list_t user_retain_groups;
int home_dir_mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
int mailbox_mode[NS_MAX];
......@@ -246,7 +248,32 @@ cb_mailbox_mode (void *data, mu_config_value_t *val)
mu_error (_("invalid mode string near %s"), p);
return 0;
}
static int
cb2_group (const char *gname, void *data)
{
mu_list_t list = data;
struct group *group;
group = getgrnam (gname);
if (!group)
mu_error (_("unknown group: %s"), gname);
else
mu_list_append (list, (void*)group->gr_gid);
return 0;
}
static int
cb_group (void *data, mu_config_value_t *arg)
{
mu_list_t *plist = data;
if (!*plist)
mu_list_create (plist);
return mu_cfg_string_value_cb (arg, cb2_group, *plist);
}
struct imap4d_srv_config
{
struct mu_srv_config m_cfg;
......@@ -392,6 +419,9 @@ static struct mu_cfg_param imap4d_cfg_param[] = {
{ "home-dir-mode", mu_cfg_callback, NULL, 0, cb_mode,
N_("File mode for creating user home directories (octal)."),
N_("mode") },
{ "retain-groups", mu_cfg_callback, &user_retain_groups, 0, cb_group,
N_("Retain these supplementary groups when switching to user privileges"),
N_("groups: list of string") },
#ifdef WITH_TLS
{ "tls", mu_cfg_callback, &tls_mode, 0, cb_tls,
N_("Kind of TLS encryption to use") },
......@@ -424,6 +454,48 @@ static struct mu_cfg_param imap4d_cfg_param[] = {
};
int
mu_get_user_groups (const char *user, mu_list_t retain, mu_list_t *pgrouplist)
{
int rc;
struct group *gr;
mu_list_t list;
if (!*pgrouplist)
{
rc = mu_list_create (pgrouplist);
if (rc)
{
mu_error(_("%s: cannot create list: %s"),
"mu_get_user_groups", mu_strerror (rc));
return rc;
}
}
list = *pgrouplist;
setgrent ();
for (rc = 0; rc == 0 && (gr = getgrent ()); )
{
char **p;
for (p = gr->gr_mem; *p; p++)
if (strcmp (*p, user) == 0)
{
if (retain && mu_list_locate (retain, (void*)gr->gr_gid, NULL))
continue;
/* FIXME: Avoid duplicating gids */
rc = mu_list_append (list, (void*)gr->gr_gid);
if (rc)
mu_error(_("%s: cannot append to list: %s"),
"mu_get_user_groups",
mu_strerror (rc));
break;
}
}
endgrent ();
return rc;
}
int
imap4d_session_setup0 ()
{
if (auth_deny_user_list &&
......@@ -502,7 +574,48 @@ imap4d_session_setup0 ()
}
if (auth_data->change_uid)
setuid (auth_data->uid);
{
struct group *gr;
mu_list_t groups = NULL;
int rc;
uid_t uid;
uid = getuid ();
if (uid != auth_data->uid)
{
rc = mu_list_create (&groups);
if (rc)
{
mu_error(_("cannot create list: %s"), mu_strerror (rc));
free (imap4d_homedir);
free (real_homedir);
return 1;
}
mu_list_append (groups, (void*)auth_data->gid);
rc = mu_get_user_groups (auth_data->name, user_retain_groups,
&groups);
if (rc)
{
/* FIXME: When mu_get_user_groups goes to the library, add a
diag message here */
free (imap4d_homedir);
free (real_homedir);
return 1;
}
gr = getgrnam ("mail");
rc = mu_switch_to_privs (auth_data->uid, gr->gr_gid, groups);
mu_list_destroy (&groups);
if (rc)
{
mu_error (_("can't switch to user %s privileges: %s"),
auth_data->name, mu_strerror (rc));
free (imap4d_homedir);
free (real_homedir);
return 1;
}
}
}
util_chdir (imap4d_homedir);
namespace_init_session (imap4d_homedir);
......
......@@ -157,9 +157,10 @@ imap4d_list (struct imap4d_session *session,
"LIST (\\NoSelect) \"%c\" \"\"",
MU_HIERARCHY_DELIMITER);
}
/* There is only one mailbox in the "INBOX" hierarchy ... INBOX. */
else if (mu_c_strcasecmp (ref, "INBOX") == 0
|| (ref[0] == 0 && mu_c_strcasecmp (wcard, "INBOX") == 0))
|| (ref[0] == 0 && mu_c_strcasecmp (wcard, "INBOX") == 0))
{
io_untagged_response (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
}
......@@ -168,8 +169,8 @@ imap4d_list (struct imap4d_session *session,
int status;
mu_folder_t folder;
char *cwd;
char *p, *q;
struct refinfo refinfo;
size_t seglen;
switch (*wcard)
{
......@@ -209,12 +210,10 @@ imap4d_list (struct imap4d_session *session,
/* Move any directory not containing a wildcard into the reference
So (ref = ~guest, wcard = Mail/folder1/%.vf) -->
(ref = ~guest/Mail/folder1, wcard = %.vf). */
for (p = wcard; (q = strpbrk (p, "/%*")) && *q == '/'; p = q + 1)
;
seglen = strcspn (wcard, "%*");
if (p > wcard)
if (seglen)
{
size_t seglen = p - wcard;
size_t reflen = strlen (ref);
int addslash = (reflen > 0 && ref[reflen-1] != '/');
size_t len = seglen + reflen + addslash + 1;
......@@ -235,6 +234,21 @@ imap4d_list (struct imap4d_session *session,
return io_completion_response (command, RESP_NO,
"The requested item could not be found.");
}
/* FIXME */
if (wcard[0] == 0)
{
char *p = strrchr (ref, '/');
if (p && p[1])
{
*p++ = 0;
wcard = p;
p = strrchr (cwd, '/');
*p = 0;
}
}
status = mu_folder_create (&folder, cwd);
if (status)
{
......
......@@ -64,13 +64,17 @@ imap4d_select0 (struct imap4d_command *command, const char *mboxname,
if (!mailbox_name)
return io_completion_response (command, RESP_NO, "Couldn't open mailbox");
if (flags & MU_STREAM_RDWR)
if (flags & MU_STREAM_WRITE)
{
status = manlock_open_mailbox (&mbox, mailbox_name, 1, flags);
if (status)
flags &= ~MU_STREAM_WRITE;
}
else
if (!(flags & MU_STREAM_WRITE))
{
status = mu_mailbox_create_default (&mbox, mailbox_name);
if (status)
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_create_default",
mailbox_name,
......
......@@ -40,8 +40,13 @@ static int
dir_exists (const char *name, const char *suf)
{
struct stat st;
char *s = maildir_mkfilename (name, suf, NULL);
int rc;
char *s;
rc = maildir_mkfilename (name, suf, NULL, &s);
if (rc)
return 0;/* FIXME: error message */
if (stat (s, &st) < 0)
return 0;
......
......@@ -21,5 +21,5 @@
#define CURSUF "cur"
#define NEWSUF "new"
extern char *maildir_mkfilename (const char *dir, const char *suffix,
const char *name);
int maildir_mkfilename (const char *dir, const char *suffix,
const char *name, char **retname);
......
......@@ -68,6 +68,7 @@
#include <mailutils/sys/mailbox.h>
#include <mailutils/sys/registrar.h>
#include <mailutils/sys/amd.h>
#include <mailutils/io.h>
#include <maildir.h>
#ifndef PATH_MAX
......@@ -237,8 +238,9 @@ read_random (void *buf, size_t size)
return rc != size;
}
char *
maildir_mkfilename (const char *directory, const char *suffix, const char *name)
int
maildir_mkfilename (const char *directory, const char *suffix, const char *name,
char **ret_name)
{
size_t size = strlen (directory) + 1 + strlen (suffix) + 1;
char *tmp;
......@@ -247,22 +249,25 @@ maildir_mkfilename (const char *directory, const char *suffix, const char *name)
size += 1 + strlen (name);
tmp = malloc (size);
if (!tmp)
return errno;
sprintf (tmp, "%s/%s", directory, suffix);
if (name)
{
strcat (tmp, "/");
strcat (tmp, name);
}
return tmp;
*ret_name = tmp;
return 0;
}
static char *
mk_info_filename (char *directory, char *suffix, char *name, int flags)
static int
mk_info_filename (char *directory, char *suffix, char *name, int flags,
char **ret_name)
{
char fbuf[info_map_size + 1];
char *tmp;
int namelen;
size_t size;
tmp = strchr (name, ':');
if (!tmp)
......@@ -270,16 +275,10 @@ mk_info_filename (char *directory, char *suffix, char *name, int flags)
else
namelen = tmp - name;
size = strlen (directory)
+ 1 + strlen (suffix)
+ 1 + namelen + 1;
flags_to_info (flags, fbuf);
size += 3 + strlen (fbuf);
tmp = malloc (size);
sprintf (tmp, "%s/%s/%*.*s:2,%s", directory, suffix, namelen, namelen, name, fbuf);
return tmp;
return mu_asprintf (ret_name, "%s/%s/%*.*s:2,%s",
directory, suffix, namelen, namelen, name, fbuf);
}
char *
......@@ -335,8 +334,7 @@ static int
maildir_cur_message_name (struct _amd_message *amsg, char **pname)
{
struct _maildir_message *msg = (struct _maildir_message *) amsg;
*pname = maildir_mkfilename (amsg->amd->name, msg->dir, msg->file_name);
return 0;
return maildir_mkfilename (amsg->amd->name, msg->dir, msg->file_name, pname);
}
static int
......@@ -350,9 +348,11 @@ maildir_new_message_name (struct _amd_message *amsg, int flags, int expunge,
*pname = NULL;
}
else if (strcmp (msg->dir, CURSUF) == 0)
*pname = mk_info_filename (amsg->amd->name, CURSUF, msg->file_name, flags);
return mk_info_filename (amsg->amd->name, CURSUF, msg->file_name, flags,
pname);
else
*pname = maildir_mkfilename (amsg->amd->name, msg->dir, msg->file_name);
return maildir_mkfilename (amsg->amd->name, msg->dir, msg->file_name,
pname);
return 0;
}
......@@ -388,7 +388,16 @@ static void
maildir_delete_file (char *dirname, char *filename)
{
struct stat st;
char *name = maildir_mkfilename (dirname, filename, NULL);
char *name;
int rc;
rc = maildir_mkfilename (dirname, filename, NULL, &name);
if (rc)
{
mu_error ("maildir: failed to create file name: %s",
mu_strerror (errno));
return;
}
if (stat (name, &st) == 0)
{
......@@ -429,15 +438,21 @@ maildir_create (struct _amd_data *amd, int flags)
for (i = 0; i < 3; i++)
{
DIR *dir;
char *tmpname = maildir_mkfilename (amd->name, dirs[i], NULL);
int rc = maildir_opendir (&dir, tmpname,
PERMS |
mu_stream_flags_to_mode (amd->mailbox->flags,
1));
int rc;
char *tmpname;
rc = maildir_mkfilename (amd->name, dirs[i], NULL, &tmpname);
if (rc)
return rc;
closedir (dir);
rc = maildir_opendir (&dir, tmpname,
PERMS |
mu_stream_flags_to_mode (amd->mailbox->flags,
1));
free (tmpname);
if (rc)
return rc;
closedir (dir);
}
return 0;
}
......@@ -454,10 +469,16 @@ maildir_msg_init (struct _amd_data *amd, struct _amd_message *amm)
char *name, *fname;
struct stat st;
int i;
int rc;
name = maildir_uniq (amd, -1);
fname = maildir_mkfilename (amd->name, NEWSUF, name);
rc = maildir_mkfilename (amd->name, NEWSUF, name, &fname);
if (rc)
{
free (name);
return rc;
}
msg->dir = TMPSUF;
for (i = 0; i < NTRIES; i++)
......@@ -483,35 +504,41 @@ maildir_msg_finish_delivery (struct _amd_data *amd, struct _amd_message *amm,
const mu_message_t orig_msg)
{
struct _maildir_message *msg = (struct _maildir_message *) amm;
char *oldname = maildir_mkfilename (amd->name, TMPSUF, msg->file_name);
char *newname;
char *oldname, *newname;
mu_attribute_t attr;
int flags;
int rc;
rc = maildir_mkfilename (amd->name, TMPSUF, msg->file_name, &oldname);
if (rc)
return rc;
if (mu_message_get_attribute (orig_msg, &attr) == 0
&& mu_attribute_is_read (attr)
&& mu_attribute_get_flags (attr, &flags) == 0)
{
msg->dir = CURSUF;
newname = mk_info_filename (amd->name, CURSUF, msg->file_name, flags);
rc = mk_info_filename (amd->name, CURSUF, msg->file_name, flags,
&newname);
}
else
{
msg->dir = NEWSUF;
newname = maildir_mkfilename (amd->name, NEWSUF, msg->file_name);
rc = maildir_mkfilename (amd->name, NEWSUF, msg->file_name, &newname);
}
unlink (newname);
if (link (oldname, newname) == 0)
unlink (oldname);
else
if (rc == 0)
{
return errno; /* FIXME? */
unlink (newname);
if (link (oldname, newname) == 0)
unlink (oldname);
else
rc = errno; /* FIXME? */
}
free (oldname);
free (newname);
return 0;
return rc;
}
......@@ -523,7 +550,11 @@ maildir_flush (struct _amd_data *amd)
int rc;
DIR *dir;
struct dirent *entry;
char *tmpname = maildir_mkfilename (amd->name, TMPSUF, NULL);
char *tmpname;
rc = maildir_mkfilename (amd->name, TMPSUF, NULL, &tmpname);
if (rc)
return rc;
rc = maildir_opendir (&dir, tmpname,
PERMS |
......@@ -561,16 +592,24 @@ maildir_deliver_new (struct _amd_data *amd, DIR *dir)
while ((entry = readdir (dir)))
{
char *oldname, *newname;
int rc;
switch (entry->d_name[0])
{
case '.':
break;
default:
oldname = maildir_mkfilename (amd->name, NEWSUF, entry->d_name);
newname = mk_info_filename (amd->name, CURSUF, entry->d_name, 0);
rename (oldname, newname);
rc = maildir_mkfilename (amd->name, NEWSUF, entry->d_name, &oldname);
if (rc)
return rc;
rc = mk_info_filename (amd->name, CURSUF, entry->d_name, 0, &newname);
if (rc)
{
free (oldname);
return rc;
}
rename (oldname, newname); /* FIXME: Error code? */
free (oldname);
free (newname);
}
......@@ -654,19 +693,27 @@ maildir_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED,
maildir_flush (amd);
/* 2nd phase: Scan and deliver messages from new */
name = maildir_mkfilename (amd->name, NEWSUF, NULL);
status = maildir_mkfilename (amd->name, NEWSUF, NULL, &name);
if (status)
return status;
status = maildir_opendir (&dir, name,
PERMS |
mu_stream_flags_to_mode (mailbox->flags, 1));
if (status == 0)
{
maildir_deliver_new (amd, dir);
if (mailbox->flags & MU_STREAM_WRITE)
maildir_deliver_new (amd, dir);
else
status = maildir_scan_dir (amd, dir, NEWSUF);
closedir (dir);
}
free (name);
name = maildir_mkfilename (amd->name, CURSUF, NULL);
status = maildir_mkfilename (amd->name, CURSUF, NULL, &name);
if (status)
return status;
/* 3rd phase: Scan cur/ */
status = maildir_opendir (&dir, name,
PERMS |
......@@ -746,7 +793,11 @@ maildir_remove (struct _amd_data *amd)
for (i = 0; rc == 0 && i < 3; i++)
{
char *name = maildir_mkfilename (amd->name, suf[i], NULL);
char *name;
rc = maildir_mkfilename (amd->name, suf[i], NULL, &name);
if (rc)
return rc;
rc = amd_remove_dir (name);
if (rc)
mu_diag_output (MU_DIAG_WARNING,
......