~ubuntu-dev/ubuntu/lucid/dovecot/lucid-201002101918

« back to all changes in this revision

Viewing changes to src/plugins/imap-acl/imap-acl-plugin.c

  • Committer: Bazaar Package Importer
  • Author(s): CHuck Short, Chuck Short
  • Date: 2009-11-06 00:47:29 UTC
  • mfrom: (4.1.9 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091106004729-i39n7v9e7d4h51f6
Tags: 1:1.2.6-1ubuntu1
* Merge from debian testing, remaining changes:
  Add new binary pkg dovecot-postfix that integrates postfix and dovecot
  automatically: (LP: #164837)
  + debian/control:
    - add new binary with short description
    - set Architecture all for dovecot-postfix (LP: #329878)
  + debian/dovecot-postfix.postinst:
    - create initial certificate symlinks to snakeoil.
    - set up postfix with postconf to:
      - use Maildir/ as the default mailbox.
      - use dovecot as the sasl authentication server.
      - use dovecot LDA (deliver).
      - use tls for smtp{d} services.
    - fix certificates paths in postfix' main.cf
    - add reject_unauth_destination to postfix' recipient restrictions
    - add reject_unknown_sender_domain to postfix' sender restrictions
    - rename configuration name on remove, delete on purge
    - restart dovecot after linking certificates
    - handle use case when postfix is unconfigurated
   + debian/dovecot-postfix.dirs: create backup directory for postfix's configuration
   + restart postfix and dovecot.
   + debian/dovecot-postfix.postrm:
     - remove all dovecot related configuration from postfix.
     - restart postfix and dovecot.
   + debian/dovecot-common.init:
     - check if /etc/dovecot/dovecot-postfix.conf exists and use it
       as the configuration file if so.
   + debian/patches/warning-ubuntu-postfix.dpatch
     - add warning about dovecot-postfix.conf in dovecot default 
       configuration file
   + debian/patches/dovecot-postfix.conf.diff:
     - Ubuntu server custom changes to the default dovecot configuration for
       better interfation with postfix
     - enable sieve plugin
   + debian/patches/dovecot-postfix.conf.diff:
     + Ubuntu server custom changes to the default dovecot configuration for
       better integration with postfix:
       - enable imap, pop3, imaps, pop3s and managesieve by default.
       - enable dovecot LDA (deliver).
       - enable SASL auth socket in postfix private directory.
   + debian/rules:
     - copy, patch and install dovecot-postfix.conf in /etc/dovecot/.
     - build architecure independent packages too
   + Use Snakeoil SSL certificates by default.
     - debian/control: Depend on ssl-cert.
     - debian/patches/ssl-cert-snakeoil.dpatch: Change default SSL cert
       paths to snakeoil.
     - debian/dovecot-common.postinst: Relax grep for SSL_* a bit.
   + Add autopkgtest to debian/tests/*.
   + Fast TearDown: Update the lsb init header to not stop in level 6.
   + Add ufw integration:
     - Created debian/dovecot-common.ufw.profile.
     - debian/rules:
       + install profile
     - debian/control:
       + Suggest ufw
   + debian/{control,rules}: enable PIE hardening.
   + dovecot-imapd, dovecot-pop3: Replaces dovecot-common (<< 1:1.1). LP: #254721
   + debian/control:
     - Update Vcs-* headers.
   + debian/rules:
     - Create emtpy stamp.h.in files in dovecot-sieve/ and dovecot-managesieve/
       if they're not there since empty files are not included in the diff.gz 
       file.
   + Add SMTP-AUTH support for Outlook (login auth mechanism)
   + Dropped:
     - debian/patches/security-CVE-2009-3235: Applied upstream.
     - debian/patches/fix-pop3-assertion.dpatch: Applied upstream.
     - dovecot-sieve and dovecot-managesieve: Use the debian patches instead.

  [Chuck Short]
  - Updated dovecot-sieve to 0.1.13.
  - Updated dovecot-managesieve to 0.11.9.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "common.h"
 
4
#include "str.h"
 
5
#include "imap-quote.h"
 
6
#include "imap-resp-code.h"
 
7
#include "commands.h"
 
8
#include "mail-storage.h"
 
9
#include "mail-namespace.h"
 
10
#include "acl-api.h"
 
11
#include "acl-storage.h"
 
12
#include "imap-acl-plugin.h"
 
13
 
 
14
#include <stdlib.h>
 
15
 
 
16
#define ERROR_NOT_ADMIN "["IMAP_RESP_CODE_NOPERM"] " \
 
17
        "You lack administrator privileges on this mailbox."
 
18
 
 
19
#define ACL_MAILBOX_OPEN_FLAGS \
 
20
        (MAILBOX_OPEN_READONLY | MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT)
 
21
 
 
22
#define IMAP_ACL_ANYONE "anyone"
 
23
#define IMAP_ACL_AUTHENTICATED "authenticated"
 
24
#define IMAP_ACL_OWNER "owner"
 
25
#define IMAP_ACL_GROUP_PREFIX "$"
 
26
#define IMAP_ACL_GROUP_OVERRIDE_PREFIX "!$"
 
27
#define IMAP_ACL_GLOBAL_PREFIX "#"
 
28
 
 
29
struct imap_acl_letter_map {
 
30
        char letter;
 
31
        const char *name;
 
32
};
 
33
 
 
34
static const struct imap_acl_letter_map imap_acl_letter_map[] = {
 
35
        { 'l', MAIL_ACL_LOOKUP },
 
36
        { 'r', MAIL_ACL_READ },
 
37
        { 'w', MAIL_ACL_WRITE },
 
38
        { 's', MAIL_ACL_WRITE_SEEN },
 
39
        { 't', MAIL_ACL_WRITE_DELETED },
 
40
        { 'i', MAIL_ACL_INSERT },
 
41
        { 'p', MAIL_ACL_POST },
 
42
        { 'e', MAIL_ACL_EXPUNGE },
 
43
        { 'k', MAIL_ACL_CREATE },
 
44
        { 'x', MAIL_ACL_DELETE },
 
45
        { 'a', MAIL_ACL_ADMIN },
 
46
        { '\0', NULL }
 
47
};
 
48
 
 
49
const char *imap_acl_plugin_version = PACKAGE_VERSION;
 
50
 
 
51
static bool acl_anyone_allow = FALSE;
 
52
 
 
53
static struct mailbox *
 
54
acl_mailbox_open_as_admin(struct client_command_context *cmd, const char *name)
 
55
{
 
56
        struct mail_storage *storage;
 
57
        struct mailbox *box;
 
58
        int ret;
 
59
 
 
60
        storage = client_find_storage(cmd, &name);
 
61
        if (storage == NULL)
 
62
                return NULL;
 
63
 
 
64
        /* Force opening the mailbox so that we can give a nicer error message
 
65
           if mailbox isn't selectable but is listable. */
 
66
        box = mailbox_open(&storage, name, NULL, ACL_MAILBOX_OPEN_FLAGS |
 
67
                           MAILBOX_OPEN_IGNORE_ACLS);
 
68
        if (box == NULL) {
 
69
                client_send_storage_error(cmd, storage);
 
70
                return NULL;
 
71
        }
 
72
 
 
73
        ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_ADMIN);
 
74
        if (ret > 0)
 
75
                return box;
 
76
 
 
77
        /* not an administrator. */
 
78
        if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP) <= 0) {
 
79
                client_send_tagline(cmd, t_strdup_printf(
 
80
                        "NO ["IMAP_RESP_CODE_NONEXISTENT"] "
 
81
                        MAIL_ERRSTR_MAILBOX_NOT_FOUND, name));
 
82
        } else {
 
83
                client_send_tagline(cmd, "NO "ERROR_NOT_ADMIN);
 
84
        }
 
85
        mailbox_close(&box);
 
86
        return NULL;
 
87
}
 
88
 
 
89
static const struct imap_acl_letter_map *
 
90
imap_acl_letter_map_find(const char *name)
 
91
{
 
92
        unsigned int i;
 
93
 
 
94
        for (i = 0; imap_acl_letter_map[i].name != NULL; i++) {
 
95
                if (strcmp(imap_acl_letter_map[i].name, name) == 0)
 
96
                        return &imap_acl_letter_map[i];
 
97
        }
 
98
        return NULL;
 
99
}
 
100
 
 
101
static void
 
102
imap_acl_write_rights_list(string_t *dest, const char *const *rights)
 
103
{
 
104
        const struct imap_acl_letter_map *map;
 
105
        unsigned int i, orig_len = str_len(dest);
 
106
        bool append_c = FALSE, append_d = FALSE;
 
107
 
 
108
        for (i = 0; rights[i] != NULL; i++) {
 
109
                /* write only letters */
 
110
                map = imap_acl_letter_map_find(rights[i]);
 
111
                if (map != NULL) {
 
112
                        str_append_c(dest, map->letter);
 
113
                        if (map->letter == 'k' || map->letter == 'x')
 
114
                                append_c = TRUE;
 
115
                        if (map->letter == 't' || map->letter == 'e')
 
116
                                append_d = TRUE;
 
117
                }
 
118
        }
 
119
        if (append_c)
 
120
                str_append_c(dest, 'c');
 
121
        if (append_d)
 
122
                str_append_c(dest, 'd');
 
123
        if (orig_len == str_len(dest))
 
124
                str_append(dest, "\"\"");
 
125
}
 
126
 
 
127
static void
 
128
imap_acl_write_right(string_t *dest, string_t *tmp,
 
129
                     const struct acl_rights *right, bool neg)
 
130
{
 
131
        const char *const *rights = neg ? right->neg_rights : right->rights;
 
132
 
 
133
        str_truncate(tmp, 0);
 
134
        if (neg) str_append_c(tmp,'-');
 
135
        if (right->global)
 
136
                str_append(tmp, IMAP_ACL_GLOBAL_PREFIX);
 
137
        switch (right->id_type) {
 
138
        case ACL_ID_ANYONE:
 
139
                str_append(tmp, IMAP_ACL_ANYONE);
 
140
                break;
 
141
        case ACL_ID_AUTHENTICATED:
 
142
                str_append(tmp, IMAP_ACL_AUTHENTICATED);
 
143
                break;
 
144
        case ACL_ID_OWNER:
 
145
                str_append(tmp, IMAP_ACL_OWNER);
 
146
                break;
 
147
        case ACL_ID_USER:
 
148
                str_append(tmp, right->identifier);
 
149
                break;
 
150
        case ACL_ID_GROUP:
 
151
                str_append(tmp, IMAP_ACL_GROUP_PREFIX);
 
152
                str_append(tmp, right->identifier);
 
153
                break;
 
154
        case ACL_ID_GROUP_OVERRIDE:
 
155
                str_append(tmp, IMAP_ACL_GROUP_OVERRIDE_PREFIX);
 
156
                str_append(tmp, right->identifier);
 
157
                break;
 
158
        case ACL_ID_TYPE_COUNT:
 
159
                i_unreached();
 
160
        }
 
161
 
 
162
        imap_quote_append(dest, str_data(tmp), str_len(tmp), FALSE);
 
163
        str_append_c(dest, ' ');
 
164
        imap_acl_write_rights_list(dest, rights);
 
165
}
 
166
 
 
167
static int
 
168
imap_acl_write_aclobj(string_t *dest, struct acl_backend *backend,
 
169
                      struct acl_object *aclobj, bool convert_owner,
 
170
                      bool add_default)
 
171
{
 
172
        struct acl_object_list_iter *iter;
 
173
        struct acl_rights rights;
 
174
        string_t *tmp;
 
175
        const char *username;
 
176
        unsigned int orig_len = str_len(dest);
 
177
        bool owner, seen_owner = FALSE, seen_positive_owner = FALSE;
 
178
        int ret;
 
179
 
 
180
        username = acl_backend_get_acl_username(backend);
 
181
        if (username == NULL)
 
182
                convert_owner = FALSE;
 
183
 
 
184
        tmp = t_str_new(128);
 
185
        iter = acl_object_list_init(aclobj);
 
186
        while ((ret = acl_object_list_next(iter, &rights)) > 0) {
 
187
                if (rights.id_type == ACL_ID_USER &&
 
188
                    acl_backend_user_name_equals(backend, rights.identifier))
 
189
                        owner = TRUE;
 
190
                else if (rights.id_type == ACL_ID_OWNER) {
 
191
                        owner = TRUE;
 
192
                        if (convert_owner) {
 
193
                                rights.id_type = ACL_ID_USER;
 
194
                                rights.identifier = username;
 
195
                        }
 
196
                } else {
 
197
                        owner = FALSE;
 
198
                }
 
199
 
 
200
                if (owner) {
 
201
                        if (seen_owner && convert_owner) {
 
202
                                /* oops, we have both owner and user=myself.
 
203
                                   can't do the conversion, so try again. */
 
204
                                str_truncate(dest, orig_len);
 
205
                                return imap_acl_write_aclobj(dest, backend,
 
206
                                                             aclobj, FALSE,
 
207
                                                             add_default);
 
208
                        }
 
209
                        seen_owner = TRUE;
 
210
                        if (rights.rights != NULL)
 
211
                                seen_positive_owner = TRUE;
 
212
                }
 
213
 
 
214
                if (rights.rights != NULL) {
 
215
                        str_append_c(dest, ' ');
 
216
                        imap_acl_write_right(dest, tmp, &rights, FALSE);
 
217
                }
 
218
                if (rights.neg_rights != NULL) {
 
219
                        str_append_c(dest, ' ');
 
220
                        imap_acl_write_right(dest, tmp, &rights, TRUE);
 
221
                }
 
222
        }
 
223
        acl_object_list_deinit(&iter);
 
224
 
 
225
        if (!seen_positive_owner && username != NULL && add_default) {
 
226
                /* no positive owner rights returned, write default ACLs */
 
227
                memset(&rights, 0, sizeof(rights));
 
228
                if (!convert_owner) {
 
229
                        rights.id_type = ACL_ID_OWNER;
 
230
                } else {
 
231
                        rights.id_type = ACL_ID_USER;
 
232
                        rights.identifier = username;
 
233
                }
 
234
                rights.rights = acl_object_get_default_rights(aclobj);
 
235
                if (rights.rights != NULL) {
 
236
                        str_append_c(dest, ' ');
 
237
                        imap_acl_write_right(dest, tmp, &rights, FALSE);
 
238
                }
 
239
        }
 
240
        return ret;
 
241
}
 
242
 
 
243
static bool cmd_getacl(struct client_command_context *cmd)
 
244
{
 
245
        struct acl_backend *backend;
 
246
        struct mail_namespace *ns;
 
247
        struct mail_storage *storage;
 
248
        struct mailbox *box;
 
249
        const char *mailbox;
 
250
        string_t *str;
 
251
        int ret;
 
252
 
 
253
        if (!client_read_string_args(cmd, 1, &mailbox))
 
254
                return FALSE;
 
255
 
 
256
        box = acl_mailbox_open_as_admin(cmd, mailbox);
 
257
        if (box == NULL)
 
258
                return TRUE;
 
259
 
 
260
        str = t_str_new(128);
 
261
        str_append(str, "* ACL ");
 
262
        imap_quote_append_string(str, mailbox, FALSE);
 
263
 
 
264
        storage = mailbox_get_storage(box);
 
265
        backend = acl_storage_get_backend(storage);
 
266
        ns = mail_storage_get_namespace(storage);
 
267
        ret = imap_acl_write_aclobj(str, backend,
 
268
                                    acl_mailbox_get_aclobj(box), TRUE,
 
269
                                    ns->type == NAMESPACE_PRIVATE);
 
270
        if (ret == 0) {
 
271
                client_send_line(cmd->client, str_c(str));
 
272
                client_send_tagline(cmd, "OK Getacl completed.");
 
273
        } else {
 
274
                client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG);
 
275
        }
 
276
        mailbox_close(&box);
 
277
        return TRUE;
 
278
}
 
279
 
 
280
static bool cmd_myrights(struct client_command_context *cmd)
 
281
{
 
282
        struct mail_storage *storage;
 
283
        struct mailbox *box;
 
284
        const char *mailbox, *real_mailbox;
 
285
        const char *const *rights;
 
286
        string_t *str;
 
287
 
 
288
        if (!client_read_string_args(cmd, 1, &mailbox))
 
289
                return FALSE;
 
290
 
 
291
        real_mailbox = mailbox;
 
292
        storage = client_find_storage(cmd, &real_mailbox);
 
293
        if (storage == NULL)
 
294
                return TRUE;
 
295
 
 
296
        box = mailbox_open(&storage, real_mailbox, NULL,
 
297
                           ACL_MAILBOX_OPEN_FLAGS | MAILBOX_OPEN_IGNORE_ACLS);
 
298
        if (box == NULL) {
 
299
                client_send_storage_error(cmd, storage);
 
300
                return TRUE;
 
301
        }
 
302
 
 
303
        if (acl_object_get_my_rights(acl_mailbox_get_aclobj(box),
 
304
                                     pool_datastack_create(), &rights) < 0) {
 
305
                client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG);
 
306
                mailbox_close(&box);
 
307
                return TRUE;
 
308
        }
 
309
        /* Post right alone doesn't give permissions to see if the mailbox
 
310
           exists or not. Only mail deliveries care about that. */
 
311
        if (*rights == NULL ||
 
312
            (strcmp(*rights, MAIL_ACL_POST) == 0 && rights[1] == NULL)) {
 
313
                client_send_tagline(cmd, t_strdup_printf(
 
314
                        "NO ["IMAP_RESP_CODE_NONEXISTENT"] "
 
315
                        MAIL_ERRSTR_MAILBOX_NOT_FOUND, real_mailbox));
 
316
                mailbox_close(&box);
 
317
                return TRUE;
 
318
        }
 
319
 
 
320
        str = t_str_new(128);
 
321
        str_append(str, "* MYRIGHTS ");
 
322
        imap_quote_append_string(str, mailbox, FALSE);
 
323
        str_append_c(str,' ');
 
324
        imap_acl_write_rights_list(str, rights);
 
325
 
 
326
        client_send_line(cmd->client, str_c(str));
 
327
        client_send_tagline(cmd, "OK Myrights completed.");
 
328
        mailbox_close(&box);
 
329
        return TRUE;
 
330
}
 
331
 
 
332
static bool cmd_listrights(struct client_command_context *cmd)
 
333
{
 
334
        struct mailbox *box;
 
335
        const char *mailbox, *identifier;
 
336
        string_t *str;
 
337
 
 
338
        if (!client_read_string_args(cmd, 2, &mailbox, &identifier))
 
339
                return FALSE;
 
340
 
 
341
        box = acl_mailbox_open_as_admin(cmd, mailbox);
 
342
        if (box == NULL)
 
343
                return TRUE;
 
344
 
 
345
        str = t_str_new(128);
 
346
        str_append(str, "* LISTRIGHTS ");
 
347
        imap_quote_append_string(str, mailbox, FALSE);
 
348
        str_append_c(str, ' ');
 
349
        imap_quote_append_string(str, identifier, FALSE);
 
350
        str_append_c(str, ' ');
 
351
        str_append(str, "\"\" l r w s t p i e k x a c d");
 
352
 
 
353
        client_send_line(cmd->client, str_c(str));
 
354
        client_send_tagline(cmd, "OK Listrights completed.");
 
355
        mailbox_close(&box);
 
356
        return TRUE;
 
357
}
 
358
 
 
359
static int
 
360
imap_acl_letters_parse(const char *letters, const char *const **rights_r,
 
361
                       const char **error_r)
 
362
{
 
363
        static const char *acl_k = MAIL_ACL_CREATE;
 
364
        static const char *acl_x = MAIL_ACL_DELETE;
 
365
        static const char *acl_e = MAIL_ACL_EXPUNGE;
 
366
        static const char *acl_t = MAIL_ACL_WRITE_DELETED;
 
367
        ARRAY_TYPE(const_string) rights;
 
368
        unsigned int i;
 
369
 
 
370
        t_array_init(&rights, 64);
 
371
        for (; *letters != '\0'; letters++) {
 
372
                for (i = 0; imap_acl_letter_map[i].name != NULL; i++) {
 
373
                        if (imap_acl_letter_map[i].letter == *letters) {
 
374
                                array_append(&rights,
 
375
                                             &imap_acl_letter_map[i].name, 1);
 
376
                                break;
 
377
                        }
 
378
                }
 
379
                if (imap_acl_letter_map[i].name == NULL) {
 
380
                        /* Handling of obsolete rights as virtual
 
381
                           rights according to RFC 4314 */
 
382
                        switch (*letters) {
 
383
                        case 'c':
 
384
                                array_append(&rights, &acl_k, 1);
 
385
                                array_append(&rights, &acl_x, 1);
 
386
                                break;
 
387
                        case 'd':
 
388
                                array_append(&rights, &acl_e, 1);
 
389
                                array_append(&rights, &acl_t, 1);
 
390
                                break;
 
391
                        default:
 
392
                                *error_r = t_strdup_printf(
 
393
                                        "Invalid ACL right: %c", *letters);
 
394
                                return -1;
 
395
                        }
 
396
                }
 
397
        }
 
398
        (void)array_append_space(&rights);
 
399
        *rights_r = array_idx(&rights, 0);
 
400
        return 0;
 
401
}
 
402
 
 
403
static int
 
404
imap_acl_identifier_parse(const char *id, struct acl_rights *rights,
 
405
                          bool check_anyone, const char **error_r)
 
406
{
 
407
        if (strncmp(id, IMAP_ACL_GLOBAL_PREFIX,
 
408
                    strlen(IMAP_ACL_GLOBAL_PREFIX)) == 0) {
 
409
                *error_r = t_strdup_printf("Global ACLs can't be modified: %s",
 
410
                                           id);
 
411
                return -1;
 
412
        }
 
413
 
 
414
        if (strcmp(id, IMAP_ACL_ANYONE) == 0) {
 
415
                if (!acl_anyone_allow && check_anyone) {
 
416
                        *error_r = "'anyone' identifier is disallowed";
 
417
                        return -1;
 
418
                }
 
419
                rights->id_type = ACL_ID_ANYONE;
 
420
        } else if (strcmp(id, IMAP_ACL_AUTHENTICATED) == 0) {
 
421
                if (!acl_anyone_allow && check_anyone) {
 
422
                        *error_r = "'authenticated' identifier is disallowed";
 
423
                        return -1;
 
424
                }
 
425
                rights->id_type = ACL_ID_AUTHENTICATED;
 
426
        } else if (strcmp(id, IMAP_ACL_OWNER) == 0)
 
427
                rights->id_type = ACL_ID_OWNER;
 
428
        else if (strncmp(id, IMAP_ACL_GROUP_PREFIX,
 
429
                         strlen(IMAP_ACL_GROUP_PREFIX)) == 0) {
 
430
                rights->id_type = ACL_ID_GROUP;
 
431
                rights->identifier = id + strlen(IMAP_ACL_GROUP_PREFIX);
 
432
        } else if (strncmp(id, IMAP_ACL_GROUP_OVERRIDE_PREFIX,
 
433
                           strlen(IMAP_ACL_GROUP_OVERRIDE_PREFIX)) == 0) {
 
434
                rights->id_type = ACL_ID_GROUP_OVERRIDE;
 
435
                rights->identifier = id +
 
436
                        strlen(IMAP_ACL_GROUP_OVERRIDE_PREFIX);
 
437
        } else {
 
438
                rights->id_type = ACL_ID_USER;
 
439
                rights->identifier = id;
 
440
        }
 
441
        return 0;
 
442
}
 
443
 
 
444
static void imap_acl_update_ensure_keep_admins(struct acl_rights_update *update)
 
445
{
 
446
        static const char *acl_admin = MAIL_ACL_ADMIN;
 
447
        const char *const *rights = update->rights.rights;
 
448
        ARRAY_TYPE(const_string) new_rights;
 
449
        unsigned int i;
 
450
 
 
451
        t_array_init(&new_rights, 64);
 
452
        for (i = 0; rights[i] != NULL; i++) {
 
453
                if (strcmp(rights[i], MAIL_ACL_ADMIN) == 0)
 
454
                        break;
 
455
                array_append(&new_rights, &rights[i], 1);
 
456
        }
 
457
 
 
458
        switch (update->modify_mode) {
 
459
        case ACL_MODIFY_MODE_REMOVE:
 
460
                if (rights[i] == NULL)
 
461
                        return;
 
462
 
 
463
                /* skip over the ADMIN removal and add the rest */
 
464
                for (i++; rights[i] != NULL; i++)
 
465
                        array_append(&new_rights, &rights[i], 1);
 
466
                break;
 
467
        case ACL_MODIFY_MODE_REPLACE:
 
468
                if (rights[i] != NULL)
 
469
                        return;
 
470
 
 
471
                /* add the missing ADMIN right */
 
472
                array_append(&new_rights, &acl_admin, 1);
 
473
                break;
 
474
        default:
 
475
                return;
 
476
        }
 
477
        (void)array_append_space(&new_rights);
 
478
        update->rights.rights = array_idx(&new_rights, 0);
 
479
}
 
480
 
 
481
static bool cmd_setacl(struct client_command_context *cmd)
 
482
{
 
483
        struct mail_namespace *ns;
 
484
        struct mail_storage *storage;
 
485
        struct mailbox *box;
 
486
        struct acl_backend *backend;
 
487
        struct acl_rights_update update;
 
488
        struct acl_rights *r;
 
489
        const char *mailbox, *identifier, *rights, *error;
 
490
        bool negative = FALSE;
 
491
 
 
492
        if (!client_read_string_args(cmd, 3, &mailbox, &identifier, &rights))
 
493
                return FALSE;
 
494
 
 
495
        if (*identifier == '\0') {
 
496
                client_send_command_error(cmd, "Invalid arguments.");
 
497
                return TRUE;
 
498
        }
 
499
 
 
500
        memset(&update, 0, sizeof(update));
 
501
        if (*identifier == '-') {
 
502
                negative = TRUE;
 
503
                identifier++;
 
504
        }
 
505
 
 
506
        switch (*rights) {
 
507
        case '-':
 
508
                update.modify_mode = ACL_MODIFY_MODE_REMOVE;
 
509
                rights++;
 
510
                break;
 
511
        case '+':
 
512
                update.modify_mode = ACL_MODIFY_MODE_ADD;
 
513
                rights++;
 
514
                break;
 
515
        default:
 
516
                update.modify_mode = ACL_MODIFY_MODE_REPLACE;
 
517
                break;
 
518
        }
 
519
 
 
520
        if (imap_acl_identifier_parse(identifier, &update.rights,
 
521
                                      TRUE, &error) < 0) {
 
522
                client_send_command_error(cmd, error);
 
523
                return TRUE;
 
524
        }
 
525
        if (imap_acl_letters_parse(rights, &update.rights.rights, &error) < 0) {
 
526
                client_send_command_error(cmd, error);
 
527
                return TRUE;
 
528
        }
 
529
        r = &update.rights;
 
530
 
 
531
        box = acl_mailbox_open_as_admin(cmd, mailbox);
 
532
        if (box == NULL)
 
533
                return TRUE;
 
534
 
 
535
        storage = mailbox_get_storage(box);
 
536
        backend = acl_storage_get_backend(storage);
 
537
        ns = mail_storage_get_namespace(storage);
 
538
        if (ns->type == NAMESPACE_PUBLIC && r->id_type == ACL_ID_OWNER) {
 
539
                client_send_tagline(cmd, "NO Public namespaces have no owner");
 
540
                mailbox_close(&box);
 
541
                return TRUE;
 
542
        }
 
543
 
 
544
        if (negative) {
 
545
                update.neg_modify_mode = update.modify_mode;
 
546
                update.modify_mode = ACL_MODIFY_MODE_REMOVE;
 
547
                update.rights.neg_rights = update.rights.rights;
 
548
                update.rights.rights = NULL;
 
549
        } else if (ns->type == NAMESPACE_PRIVATE && r->rights != NULL &&
 
550
                   ((r->id_type == ACL_ID_USER &&
 
551
                     acl_backend_user_name_equals(backend, r->identifier)) ||
 
552
                    (r->id_type == ACL_ID_OWNER &&
 
553
                     strcmp(acl_backend_get_acl_username(backend),
 
554
                            ns->user->username) == 0))) {
 
555
                /* make sure client doesn't (accidentally) remove admin
 
556
                   privileges from its own mailboxes */
 
557
                imap_acl_update_ensure_keep_admins(&update);
 
558
        }
 
559
 
 
560
        if (acl_object_update(acl_mailbox_get_aclobj(box), &update) < 0)
 
561
                client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG);
 
562
        else
 
563
                client_send_tagline(cmd, "OK Setacl complete.");
 
564
        mailbox_close(&box);
 
565
        return TRUE;
 
566
}
 
567
 
 
568
static bool cmd_deleteacl(struct client_command_context *cmd)
 
569
{
 
570
        struct mailbox *box;
 
571
        struct acl_rights_update update;
 
572
        const char *mailbox, *identifier, *error;
 
573
 
 
574
        if (!client_read_string_args(cmd, 2, &mailbox, &identifier))
 
575
                return FALSE;
 
576
        if (*identifier == '\0') {
 
577
                client_send_command_error(cmd, "Invalid arguments.");
 
578
                return TRUE;
 
579
        }
 
580
 
 
581
        memset(&update, 0, sizeof(update));
 
582
        if (*identifier != '-')
 
583
                update.modify_mode = ACL_MODIFY_MODE_CLEAR;
 
584
        else {
 
585
                update.neg_modify_mode = ACL_MODIFY_MODE_CLEAR;
 
586
                identifier++;
 
587
        }
 
588
 
 
589
        if (imap_acl_identifier_parse(identifier, &update.rights,
 
590
                                      FALSE, &error) < 0) {
 
591
                client_send_command_error(cmd, error);
 
592
                return TRUE;
 
593
        }
 
594
 
 
595
        box = acl_mailbox_open_as_admin(cmd, mailbox);
 
596
        if (box == NULL)
 
597
                return TRUE;
 
598
 
 
599
        if (acl_object_update(acl_mailbox_get_aclobj(box), &update) < 0)
 
600
                client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG);
 
601
        else
 
602
                client_send_tagline(cmd, "OK Deleteacl complete.");
 
603
        mailbox_close(&box);
 
604
        return TRUE;
 
605
}
 
606
 
 
607
void imap_acl_plugin_init(void)
 
608
{
 
609
        const char *env;
 
610
 
 
611
        if (getenv("ACL") == NULL)
 
612
                return;
 
613
 
 
614
        env = getenv("ACL_ANYONE");
 
615
        if (env != NULL)
 
616
                acl_anyone_allow = strcmp(env, "allow") == 0;
 
617
 
 
618
        str_append(capability_string, " ACL RIGHTS=texk");
 
619
 
 
620
        command_register("LISTRIGHTS", cmd_listrights, 0);
 
621
        command_register("GETACL", cmd_getacl, 0);
 
622
        command_register("MYRIGHTS", cmd_myrights, 0);
 
623
        command_register("SETACL", cmd_setacl, 0);
 
624
        command_register("DELETEACL", cmd_deleteacl, 0);
 
625
}
 
626
 
 
627
void imap_acl_plugin_deinit(void)
 
628
{
 
629
        if (getenv("ACL") == NULL)
 
630
                return;
 
631
 
 
632
        command_unregister("GETACL");
 
633
        command_unregister("MYRIGHTS");
 
634
        command_unregister("SETACL");
 
635
        command_unregister("DELETEACL");
 
636
        command_unregister("LISTRIGHTS");
 
637
}