1 /* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "str.h"
6 #include "strescape.h"
7 #include "hash.h"
8 #include "mail-user.h"
9 #include "mailbox-list.h"
10 #include "acl-global-file.h"
11 #include "acl-cache.h"
12 #include "acl-api-private.h"
13
14 struct acl_letter_map {
15 char letter;
16 const char *name;
17 };
18
19 static const struct acl_letter_map acl_letter_map[] = {
20 { 'l', MAIL_ACL_LOOKUP },
21 { 'r', MAIL_ACL_READ },
22 { 'w', MAIL_ACL_WRITE },
23 { 's', MAIL_ACL_WRITE_SEEN },
24 { 't', MAIL_ACL_WRITE_DELETED },
25 { 'i', MAIL_ACL_INSERT },
26 { 'p', MAIL_ACL_POST },
27 { 'e', MAIL_ACL_EXPUNGE },
28 { 'k', MAIL_ACL_CREATE },
29 { 'x', MAIL_ACL_DELETE },
30 { 'a', MAIL_ACL_ADMIN },
31 { '\0', NULL }
32 };
33
acl_object_init_from_name(struct acl_backend * backend,const char * name)34 struct acl_object *acl_object_init_from_name(struct acl_backend *backend,
35 const char *name)
36 {
37 return backend->v.object_init(backend, name);
38 }
39
acl_object_init_from_parent(struct acl_backend * backend,const char * child_name)40 struct acl_object *acl_object_init_from_parent(struct acl_backend *backend,
41 const char *child_name)
42 {
43 return backend->v.object_init_parent(backend, child_name);
44 }
45
acl_object_deinit(struct acl_object ** _aclobj)46 void acl_object_deinit(struct acl_object **_aclobj)
47 {
48 struct acl_object *aclobj = *_aclobj;
49
50 *_aclobj = NULL;
51 aclobj->backend->v.object_deinit(aclobj);
52 }
53
acl_object_have_right(struct acl_object * aclobj,unsigned int right_idx)54 int acl_object_have_right(struct acl_object *aclobj, unsigned int right_idx)
55 {
56 struct acl_backend *backend = aclobj->backend;
57 const struct acl_mask *have_mask;
58 unsigned int read_idx;
59
60 if (backend->v.object_refresh_cache(aclobj) < 0)
61 return -1;
62
63 have_mask = acl_cache_get_my_rights(backend->cache, aclobj->name);
64 if (have_mask == NULL) {
65 if (acl_backend_get_default_rights(backend, &have_mask) < 0)
66 return -1;
67 }
68
69 if (acl_cache_mask_isset(have_mask, right_idx))
70 return 1;
71
72 if (mailbox_list_get_user(aclobj->backend->list)->dsyncing) {
73 /* when dsync is running on a shared mailbox, it must be able
74 to do everything inside it. however, dsync shouldn't touch
75 mailboxes where user doesn't have any read access, because
76 that could make them readable on the replica. */
77 read_idx = acl_backend_lookup_right(aclobj->backend,
78 MAIL_ACL_READ);
79 if (acl_cache_mask_isset(have_mask, read_idx))
80 return 1;
81 }
82 return 0;
83 }
84
85 const char *const *
acl_backend_mask_get_names(struct acl_backend * backend,const struct acl_mask * mask,pool_t pool)86 acl_backend_mask_get_names(struct acl_backend *backend,
87 const struct acl_mask *mask, pool_t pool)
88 {
89 const char *const *names;
90 const char **buf, **rights;
91 unsigned int names_count, count, i, j, name_idx;
92
93 names = acl_cache_get_names(backend->cache, &names_count);
94 buf = t_new(const char *, (mask->size * CHAR_BIT) + 1);
95 count = 0;
96 for (i = 0, name_idx = 0; i < mask->size; i++) {
97 if (mask->mask[i] == 0)
98 name_idx += CHAR_BIT;
99 else {
100 for (j = 1; j < (1 << CHAR_BIT); j <<= 1, name_idx++) {
101 if ((mask->mask[i] & j) == 0)
102 continue;
103
104 /* @UNSAFE */
105 i_assert(name_idx < names_count);
106 buf[count++] = p_strdup(pool, names[name_idx]);
107 }
108 }
109 }
110
111 /* @UNSAFE */
112 rights = p_new(pool, const char *, count + 1);
113 memcpy(rights, buf, count * sizeof(const char *));
114 return rights;
115 }
116
acl_object_get_my_rights_real(struct acl_object * aclobj,pool_t pool,const char * const ** rights_r)117 static int acl_object_get_my_rights_real(struct acl_object *aclobj, pool_t pool,
118 const char *const **rights_r)
119 {
120 struct acl_backend *backend = aclobj->backend;
121 const struct acl_mask *mask;
122
123 if (backend->v.object_refresh_cache(aclobj) < 0)
124 return -1;
125
126 mask = acl_cache_get_my_rights(backend->cache, aclobj->name);
127 if (mask == NULL) {
128 if (acl_backend_get_default_rights(backend, &mask) < 0)
129 return -1;
130 }
131
132 *rights_r = acl_backend_mask_get_names(backend, mask, pool);
133 return 0;
134 }
135
acl_object_get_my_rights(struct acl_object * aclobj,pool_t pool,const char * const ** rights_r)136 int acl_object_get_my_rights(struct acl_object *aclobj, pool_t pool,
137 const char *const **rights_r)
138 {
139 int ret;
140
141 if (pool->datastack_pool)
142 return acl_object_get_my_rights_real(aclobj, pool, rights_r);
143 T_BEGIN {
144 ret = acl_object_get_my_rights_real(aclobj, pool, rights_r);
145 } T_END;
146 return ret;
147 }
148
acl_object_get_default_rights(struct acl_object * aclobj)149 const char *const *acl_object_get_default_rights(struct acl_object *aclobj)
150 {
151 return acl_backend_mask_get_names(aclobj->backend,
152 aclobj->backend->default_aclmask,
153 pool_datastack_create());
154 }
155
acl_object_last_changed(struct acl_object * aclobj,time_t * last_changed_r)156 int acl_object_last_changed(struct acl_object *aclobj, time_t *last_changed_r)
157 {
158 return aclobj->backend->v.last_changed(aclobj, last_changed_r);
159 }
160
acl_object_update(struct acl_object * aclobj,const struct acl_rights_update * update)161 int acl_object_update(struct acl_object *aclobj,
162 const struct acl_rights_update *update)
163 {
164 return aclobj->backend->v.object_update(aclobj, update);
165 }
166
acl_object_list_init(struct acl_object * aclobj)167 struct acl_object_list_iter *acl_object_list_init(struct acl_object *aclobj)
168 {
169 return aclobj->backend->v.object_list_init(aclobj);
170 }
171
acl_object_list_next(struct acl_object_list_iter * iter,struct acl_rights * rights_r)172 bool acl_object_list_next(struct acl_object_list_iter *iter,
173 struct acl_rights *rights_r)
174 {
175 if (iter->failed)
176 return FALSE;
177
178 return iter->aclobj->backend->v.object_list_next(iter, rights_r);
179 }
180
acl_object_list_deinit(struct acl_object_list_iter ** _iter)181 int acl_object_list_deinit(struct acl_object_list_iter **_iter)
182 {
183 struct acl_object_list_iter *iter = *_iter;
184
185 *_iter = NULL;
186 return iter->aclobj->backend->v.object_list_deinit(iter);
187 }
188
189 struct acl_object_list_iter *
acl_default_object_list_init(struct acl_object * aclobj)190 acl_default_object_list_init(struct acl_object *aclobj)
191 {
192 struct acl_object_list_iter *iter;
193 const struct acl_rights *aclobj_rights;
194 unsigned int i;
195 pool_t pool;
196
197 pool = pool_alloconly_create("acl object list", 512);
198 iter = p_new(pool, struct acl_object_list_iter, 1);
199 iter->pool = pool;
200 iter->aclobj = aclobj;
201
202 if (!array_is_created(&aclobj->rights)) {
203 /* we may have the object cached, but we don't have all the
204 rights read into memory */
205 acl_cache_flush(aclobj->backend->cache, aclobj->name);
206 }
207
208 if (aclobj->backend->v.object_refresh_cache(aclobj) < 0)
209 iter->failed = TRUE;
210
211 aclobj_rights = array_get(&aclobj->rights, &iter->count);
212 if (iter->count > 0) {
213 iter->rights = p_new(pool, struct acl_rights, iter->count);
214 for (i = 0; i < iter->count; i++)
215 acl_rights_dup(&aclobj_rights[i], pool, &iter->rights[i]);
216 } else
217 iter->empty = TRUE;
218 return iter;
219 }
220
acl_default_object_list_next(struct acl_object_list_iter * iter,struct acl_rights * rights_r)221 bool acl_default_object_list_next(struct acl_object_list_iter *iter,
222 struct acl_rights *rights_r)
223 {
224 if (iter->failed)
225 return FALSE;
226
227 if (iter->idx == iter->count)
228 return FALSE;
229 *rights_r = iter->rights[iter->idx++];
230 return TRUE;
231 }
232
acl_default_object_list_deinit(struct acl_object_list_iter * iter)233 int acl_default_object_list_deinit(struct acl_object_list_iter *iter)
234 {
235 int ret = 0;
236 if (iter->failed)
237 ret = -1;
238 else if (iter->empty)
239 ret = 0;
240 else
241 ret = 1;
242
243 pool_unref(&iter->pool);
244 return ret;
245 }
246
247 struct acl_mailbox_list_context *
acl_backend_nonowner_lookups_iter_init(struct acl_backend * backend)248 acl_backend_nonowner_lookups_iter_init(struct acl_backend *backend)
249 {
250 return backend->v.nonowner_lookups_iter_init(backend);
251 }
252
acl_backend_nonowner_lookups_iter_next(struct acl_mailbox_list_context * ctx,const char ** name_r)253 bool acl_backend_nonowner_lookups_iter_next(struct acl_mailbox_list_context *ctx,
254 const char **name_r)
255 {
256 return ctx->backend->v.nonowner_lookups_iter_next(ctx, name_r);
257 }
258
acl_backend_nonowner_lookups_iter_deinit(struct acl_mailbox_list_context ** _ctx)259 int acl_backend_nonowner_lookups_iter_deinit(struct acl_mailbox_list_context **_ctx)
260 {
261 struct acl_mailbox_list_context *ctx = *_ctx;
262
263 *_ctx = NULL;
264 return ctx->backend->v.nonowner_lookups_iter_deinit(ctx);
265 }
266
acl_backend_nonowner_lookups_rebuild(struct acl_backend * backend)267 int acl_backend_nonowner_lookups_rebuild(struct acl_backend *backend)
268 {
269 return backend->v.nonowner_lookups_rebuild(backend);
270 }
271
acl_rights_write_id(string_t * dest,const struct acl_rights * right)272 void acl_rights_write_id(string_t *dest, const struct acl_rights *right)
273 {
274 switch (right->id_type) {
275 case ACL_ID_ANYONE:
276 str_append(dest, ACL_ID_NAME_ANYONE);
277 break;
278 case ACL_ID_AUTHENTICATED:
279 str_append(dest, ACL_ID_NAME_AUTHENTICATED);
280 break;
281 case ACL_ID_OWNER:
282 str_append(dest, ACL_ID_NAME_OWNER);
283 break;
284 case ACL_ID_USER:
285 str_append(dest, ACL_ID_NAME_USER_PREFIX);
286 str_append(dest, right->identifier);
287 break;
288 case ACL_ID_GROUP:
289 str_append(dest, ACL_ID_NAME_GROUP_PREFIX);
290 str_append(dest, right->identifier);
291 break;
292 case ACL_ID_GROUP_OVERRIDE:
293 str_append(dest, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX);
294 str_append(dest, right->identifier);
295 break;
296 case ACL_ID_TYPE_COUNT:
297 i_unreached();
298 }
299 }
300
acl_rights_get_id(const struct acl_rights * right)301 const char *acl_rights_get_id(const struct acl_rights *right)
302 {
303 string_t *str = t_str_new(32);
304
305 acl_rights_write_id(str, right);
306 return str_c(str);
307 }
308
is_standard_right(const char * name)309 static bool is_standard_right(const char *name)
310 {
311 unsigned int i;
312
313 for (i = 0; all_mailbox_rights[i] != NULL; i++) {
314 if (strcmp(all_mailbox_rights[i], name) == 0)
315 return TRUE;
316 }
317 return FALSE;
318 }
319
acl_rights_update_import(struct acl_rights_update * update,const char * id,const char * const * rights,const char ** error_r)320 int acl_rights_update_import(struct acl_rights_update *update,
321 const char *id, const char *const *rights,
322 const char **error_r)
323 {
324 ARRAY_TYPE(const_string) dest_rights, dest_neg_rights, *dest;
325 unsigned int i, j;
326
327 if (acl_identifier_parse(id, &update->rights) < 0) {
328 *error_r = t_strdup_printf("Invalid ID: %s", id);
329 return -1;
330 }
331 if (rights == NULL) {
332 update->modify_mode = ACL_MODIFY_MODE_CLEAR;
333 update->neg_modify_mode = ACL_MODIFY_MODE_CLEAR;
334 return 0;
335 }
336
337 t_array_init(&dest_rights, 8);
338 t_array_init(&dest_neg_rights, 8);
339 for (i = 0; rights[i] != NULL; i++) {
340 const char *right = rights[i];
341
342 if (right[0] != '-')
343 dest = &dest_rights;
344 else {
345 right++;
346 dest = &dest_neg_rights;
347 }
348 if (strcmp(right, "all") != 0) {
349 if (*right == ':') {
350 /* non-standard right */
351 right++;
352 array_push_back(dest, &right);
353 } else if (is_standard_right(right)) {
354 array_push_back(dest, &right);
355 } else {
356 *error_r = t_strdup_printf("Invalid right '%s'",
357 right);
358 return -1;
359 }
360 } else {
361 for (j = 0; all_mailbox_rights[j] != NULL; j++)
362 array_push_back(dest, &all_mailbox_rights[j]);
363 }
364 }
365 if (array_count(&dest_rights) > 0) {
366 array_append_zero(&dest_rights);
367 update->rights.rights = array_front(&dest_rights);
368 } else if (update->modify_mode == ACL_MODIFY_MODE_REPLACE) {
369 update->modify_mode = ACL_MODIFY_MODE_CLEAR;
370 }
371 if (array_count(&dest_neg_rights) > 0) {
372 array_append_zero(&dest_neg_rights);
373 update->rights.neg_rights = array_front(&dest_neg_rights);
374 } else if (update->neg_modify_mode == ACL_MODIFY_MODE_REPLACE) {
375 update->neg_modify_mode = ACL_MODIFY_MODE_CLEAR;
376 }
377 return 0;
378 }
379
acl_rights_export(const struct acl_rights * rights)380 const char *acl_rights_export(const struct acl_rights *rights)
381 {
382 string_t *str = t_str_new(128);
383
384 if (rights->rights != NULL)
385 str_append(str, t_strarray_join(rights->rights, " "));
386 if (rights->neg_rights != NULL && rights->neg_rights[0] != NULL) {
387 if (str_len(str) > 0)
388 str_append_c(str, ' ');
389 str_append_c(str, '-');
390 str_append(str, t_strarray_join(rights->neg_rights, " -"));
391 }
392 return str_c(str);
393 }
394
acl_rights_parse_line(const char * line,pool_t pool,struct acl_rights * rights_r,const char ** error_r)395 int acl_rights_parse_line(const char *line, pool_t pool,
396 struct acl_rights *rights_r, const char **error_r)
397 {
398 const char *id_str, *const *right_names, *error = NULL;
399
400 /* <id> [<imap acls>] [:<named acls>] */
401 if (*line == '"') {
402 line++;
403 if (str_unescape_next(&line, &id_str) < 0 ||
404 (line[0] != ' ' && line[0] != '\0')) {
405 *error_r = "Invalid quoted ID";
406 return -1;
407 }
408 if (line[0] == ' ')
409 line++;
410 } else {
411 id_str = line;
412 line = strchr(id_str, ' ');
413 if (line == NULL)
414 line = "";
415 else
416 id_str = t_strdup_until(id_str, line++);
417 }
418
419 i_zero(rights_r);
420
421 right_names = acl_right_names_parse(pool, line, &error);
422 if (*id_str != '-')
423 rights_r->rights = right_names;
424 else {
425 id_str++;
426 rights_r->neg_rights = right_names;
427 }
428
429 if (acl_identifier_parse(id_str, rights_r) < 0)
430 error = t_strdup_printf("Unknown ID '%s'", id_str);
431
432 if (error != NULL) {
433 *error_r = error;
434 return -1;
435 }
436
437 rights_r->identifier = p_strdup(pool, rights_r->identifier);
438 return 0;
439 }
440
acl_rights_dup(const struct acl_rights * src,pool_t pool,struct acl_rights * dest_r)441 void acl_rights_dup(const struct acl_rights *src,
442 pool_t pool, struct acl_rights *dest_r)
443 {
444 i_zero(dest_r);
445 dest_r->id_type = src->id_type;
446 dest_r->identifier = p_strdup(pool, src->identifier);
447 dest_r->rights = src->rights == NULL ? NULL :
448 p_strarray_dup(pool, src->rights);
449 dest_r->neg_rights = src->neg_rights == NULL ? NULL :
450 p_strarray_dup(pool, src->neg_rights);
451 dest_r->global = src->global;
452 }
453
acl_rights_cmp(const struct acl_rights * r1,const struct acl_rights * r2)454 int acl_rights_cmp(const struct acl_rights *r1, const struct acl_rights *r2)
455 {
456 int ret;
457
458 if (r1->global != r2->global) {
459 /* globals have higher priority than locals */
460 return r1->global ? 1 : -1;
461 }
462
463 ret = (int)r1->id_type - (int)r2->id_type;
464 if (ret != 0)
465 return ret;
466
467 return null_strcmp(r1->identifier, r2->identifier);
468 }
469
acl_rights_sort(struct acl_object * aclobj)470 void acl_rights_sort(struct acl_object *aclobj)
471 {
472 struct acl_rights *rights;
473 unsigned int i, dest, count;
474
475 if (!array_is_created(&aclobj->rights))
476 return;
477
478 array_sort(&aclobj->rights, acl_rights_cmp);
479
480 /* merge identical identifiers */
481 rights = array_get_modifiable(&aclobj->rights, &count);
482 for (dest = 0, i = 1; i < count; i++) {
483 if (acl_rights_cmp(&rights[i], &rights[dest]) == 0) {
484 /* add i's rights to dest and delete i */
485 acl_right_names_merge(aclobj->rights_pool,
486 &rights[dest].rights,
487 rights[i].rights, FALSE);
488 acl_right_names_merge(aclobj->rights_pool,
489 &rights[dest].neg_rights,
490 rights[i].neg_rights, FALSE);
491 } else {
492 if (++dest != i)
493 rights[dest] = rights[i];
494 }
495 }
496 if (++dest < count)
497 array_delete(&aclobj->rights, dest, count - dest);
498 }
499
acl_rights_has_nonowner_lookup_changes(const struct acl_rights * rights)500 bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights)
501 {
502 const char *const *p;
503
504 if (rights->id_type == ACL_ID_OWNER) {
505 /* ignore owner rights */
506 return FALSE;
507 }
508
509 if (rights->rights == NULL)
510 return FALSE;
511
512 for (p = rights->rights; *p != NULL; p++) {
513 if (strcmp(*p, MAIL_ACL_LOOKUP) == 0)
514 return TRUE;
515 }
516 return FALSE;
517 }
518
acl_identifier_parse(const char * line,struct acl_rights * rights)519 int acl_identifier_parse(const char *line, struct acl_rights *rights)
520 {
521 if (str_begins(line, ACL_ID_NAME_USER_PREFIX)) {
522 rights->id_type = ACL_ID_USER;
523 rights->identifier = line + 5;
524 } else if (strcmp(line, ACL_ID_NAME_OWNER) == 0) {
525 rights->id_type = ACL_ID_OWNER;
526 } else if (str_begins(line, ACL_ID_NAME_GROUP_PREFIX)) {
527 rights->id_type = ACL_ID_GROUP;
528 rights->identifier = line + 6;
529 } else if (str_begins(line, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX)) {
530 rights->id_type = ACL_ID_GROUP_OVERRIDE;
531 rights->identifier = line + 15;
532 } else if (strcmp(line, ACL_ID_NAME_AUTHENTICATED) == 0) {
533 rights->id_type = ACL_ID_AUTHENTICATED;
534 } else if (strcmp(line, ACL_ID_NAME_ANYONE) == 0 ||
535 strcmp(line, "anonymous") == 0) {
536 rights->id_type = ACL_ID_ANYONE;
537 } else {
538 return -1;
539 }
540 return 0;
541 }
542
543 static const char *const *
acl_right_names_alloc(pool_t pool,ARRAY_TYPE (const_string)* rights_arr,bool dup_strings)544 acl_right_names_alloc(pool_t pool, ARRAY_TYPE(const_string) *rights_arr,
545 bool dup_strings)
546 {
547 const char **ret, *const *rights;
548 unsigned int i, dest, count;
549
550 /* sort the rights first so we can easily drop duplicates */
551 array_sort(rights_arr, i_strcmp_p);
552
553 /* @UNSAFE */
554 rights = array_get(rights_arr, &count);
555 ret = p_new(pool, const char *, count + 1);
556 if (count > 0) {
557 ret[0] = rights[0];
558 for (i = dest = 1; i < count; i++) {
559 if (strcmp(rights[i-1], rights[i]) != 0)
560 ret[dest++] = rights[i];
561 }
562 ret[dest] = NULL;
563 if (dup_strings) {
564 for (i = 0; i < dest; i++)
565 ret[i] = p_strdup(pool, ret[i]);
566 }
567 }
568 return ret;
569 }
570
571 const char *const *
acl_right_names_parse(pool_t pool,const char * acl,const char ** error_r)572 acl_right_names_parse(pool_t pool, const char *acl, const char **error_r)
573 {
574 ARRAY_TYPE(const_string) rights;
575 const char *const *names;
576 unsigned int i;
577
578 /* parse IMAP ACL list */
579 while (*acl == ' ' || *acl == '\t')
580 acl++;
581
582 t_array_init(&rights, 64);
583 while (*acl != '\0' && *acl != ' ' && *acl != '\t' && *acl != ':') {
584 for (i = 0; acl_letter_map[i].letter != '\0'; i++) {
585 if (acl_letter_map[i].letter == *acl)
586 break;
587 }
588
589 if (acl_letter_map[i].letter == '\0') {
590 *error_r = t_strdup_printf("Unknown ACL '%c'", *acl);
591 return NULL;
592 }
593
594 array_push_back(&rights, &acl_letter_map[i].name);
595 acl++;
596 }
597 while (*acl == ' ' || *acl == '\t') acl++;
598
599 if (*acl != '\0') {
600 /* parse our own extended ACLs */
601 if (*acl != ':') {
602 *error_r = "Missing ':' prefix in ACL extensions";
603 return NULL;
604 }
605
606 names = t_strsplit_spaces(acl + 1, ", \t");
607 for (; *names != NULL; names++) {
608 const char *name = p_strdup(pool, *names);
609 array_push_back(&rights, &name);
610 }
611 }
612
613 return acl_right_names_alloc(pool, &rights, FALSE);
614 }
615
acl_right_names_write(string_t * dest,const char * const * rights)616 void acl_right_names_write(string_t *dest, const char *const *rights)
617 {
618 char c2[2];
619 unsigned int i, j, pos;
620
621 c2[1] = '\0';
622 pos = str_len(dest);
623 for (i = 0; rights[i] != NULL; i++) {
624 /* use letters if possible */
625 for (j = 0; acl_letter_map[j].name != NULL; j++) {
626 if (strcmp(rights[i], acl_letter_map[j].name) == 0) {
627 c2[0] = acl_letter_map[j].letter;
628 str_insert(dest, pos, c2);
629 pos++;
630 break;
631 }
632 }
633 if (acl_letter_map[j].name == NULL) {
634 /* fallback to full name */
635 str_append_c(dest, ' ');
636 str_append(dest, rights[i]);
637 }
638 }
639 if (pos + 1 < str_len(dest)) {
640 c2[0] = ':';
641 str_insert(dest, pos + 1, c2);
642 }
643 }
644
acl_right_names_merge(pool_t pool,const char * const ** destp,const char * const * src,bool dup_strings)645 void acl_right_names_merge(pool_t pool, const char *const **destp,
646 const char *const *src, bool dup_strings)
647 {
648 const char *const *dest = *destp;
649 ARRAY_TYPE(const_string) rights;
650 unsigned int i;
651
652 t_array_init(&rights, 64);
653 if (dest != NULL) {
654 for (i = 0; dest[i] != NULL; i++)
655 array_push_back(&rights, &dest[i]);
656 }
657 if (src != NULL) {
658 for (i = 0; src[i] != NULL; i++)
659 array_push_back(&rights, &src[i]);
660 }
661
662 *destp = acl_right_names_alloc(pool, &rights, dup_strings);
663 }
664
acl_right_names_modify(pool_t pool,const char * const ** rightsp,const char * const * modify_rights,enum acl_modify_mode modify_mode)665 bool acl_right_names_modify(pool_t pool,
666 const char *const **rightsp,
667 const char *const *modify_rights,
668 enum acl_modify_mode modify_mode)
669 {
670 const char *const *old_rights = *rightsp;
671 const char *const *new_rights = NULL;
672 const char *null = NULL;
673 ARRAY_TYPE(const_string) rights;
674 unsigned int i, j;
675
676 if (modify_rights == NULL && modify_mode != ACL_MODIFY_MODE_CLEAR) {
677 /* nothing to do here */
678 return FALSE;
679 }
680
681 switch (modify_mode) {
682 case ACL_MODIFY_MODE_REMOVE:
683 if (old_rights == NULL || *old_rights == NULL) {
684 /* nothing to do */
685 return FALSE;
686 }
687 t_array_init(&rights, 64);
688 for (i = 0; old_rights[i] != NULL; i++) {
689 for (j = 0; modify_rights[j] != NULL; j++) {
690 if (strcmp(old_rights[i], modify_rights[j]) == 0)
691 break;
692 }
693 if (modify_rights[j] == NULL)
694 array_push_back(&rights, &old_rights[i]);
695 }
696 new_rights = &null;
697 modify_rights = array_count(&rights) == 0 ? NULL :
698 array_front(&rights);
699 acl_right_names_merge(pool, &new_rights, modify_rights, TRUE);
700 break;
701 case ACL_MODIFY_MODE_ADD:
702 new_rights = old_rights;
703 acl_right_names_merge(pool, &new_rights, modify_rights, TRUE);
704 break;
705 case ACL_MODIFY_MODE_REPLACE:
706 new_rights = &null;
707 acl_right_names_merge(pool, &new_rights, modify_rights, TRUE);
708 break;
709 case ACL_MODIFY_MODE_CLEAR:
710 if (*rightsp == NULL) {
711 /* ACL didn't exist before either */
712 return FALSE;
713 }
714 *rightsp = NULL;
715 return TRUE;
716 }
717 i_assert(new_rights != NULL);
718 *rightsp = new_rights;
719
720 if (old_rights == NULL)
721 return new_rights[0] != NULL;
722
723 /* see if anything changed */
724 for (i = 0; old_rights[i] != NULL && new_rights[i] != NULL; i++) {
725 if (strcmp(old_rights[i], new_rights[i]) != 0)
726 return TRUE;
727 }
728 return old_rights[i] != NULL || new_rights[i] != NULL;
729 }
730
apply_owner_default_rights(struct acl_object * aclobj)731 static void apply_owner_default_rights(struct acl_object *aclobj)
732 {
733 struct acl_rights_update ru;
734 const char *null = NULL;
735
736 i_zero(&ru);
737 ru.modify_mode = ACL_MODIFY_MODE_REPLACE;
738 ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE;
739 ru.rights.id_type = ACL_ID_OWNER;
740 ru.rights.rights = aclobj->backend->default_rights;
741 ru.rights.neg_rights = &null;
742 acl_cache_update(aclobj->backend->cache, aclobj->name, &ru);
743 }
744
acl_object_rebuild_cache(struct acl_object * aclobj)745 void acl_object_rebuild_cache(struct acl_object *aclobj)
746 {
747 struct acl_rights_update ru;
748 enum acl_modify_mode add_mode;
749 const struct acl_rights *rights, *prev_match = NULL;
750 unsigned int i, count;
751 bool first_global = TRUE;
752
753 acl_cache_flush(aclobj->backend->cache, aclobj->name);
754
755 if (!array_is_created(&aclobj->rights))
756 return;
757
758 /* Rights are sorted by their 1) locals first, globals next,
759 2) acl_id_type. We'll apply only the rights matching ourself.
760
761 Every time acl_id_type or local/global changes, the new ACLs will
762 replace all of the existing ACLs. Basically this means that if
763 user belongs to multiple matching groups or group-overrides, their
764 ACLs are merged. In all other situations the ACLs are replaced
765 (because there aren't duplicate rights entries and a user can't
766 match multiple usernames). */
767 i_zero(&ru);
768 rights = array_get(&aclobj->rights, &count);
769 if (!acl_backend_user_is_owner(aclobj->backend))
770 i = 0;
771 else {
772 /* we're the owner. skip over all rights entries until we
773 reach ACL_ID_OWNER or higher, or alternatively when we
774 reach a global ACL (even ACL_ID_ANYONE overrides owner's
775 rights if it's global) */
776 for (i = 0; i < count; i++) {
777 if (rights[i].id_type >= ACL_ID_OWNER ||
778 rights[i].global)
779 break;
780 }
781 apply_owner_default_rights(aclobj);
782 /* now continue applying the rest of the rights,
783 if there are any */
784 }
785 for (; i < count; i++) {
786 if (!acl_backend_rights_match_me(aclobj->backend, &rights[i]))
787 continue;
788
789 if (prev_match == NULL ||
790 prev_match->id_type != rights[i].id_type ||
791 prev_match->global != rights[i].global) {
792 /* replace old ACLs */
793 add_mode = ACL_MODIFY_MODE_REPLACE;
794 } else {
795 /* merging to existing ACLs */
796 i_assert(rights[i].id_type == ACL_ID_GROUP ||
797 rights[i].id_type == ACL_ID_GROUP_OVERRIDE);
798 add_mode = ACL_MODIFY_MODE_ADD;
799 }
800 prev_match = &rights[i];
801
802 /* If [neg_]rights is NULL it needs to be ignored.
803 The easiest way to do that is to just mark it with
804 REMOVE mode */
805 ru.modify_mode = rights[i].rights == NULL ?
806 ACL_MODIFY_MODE_REMOVE : add_mode;
807 ru.neg_modify_mode = rights[i].neg_rights == NULL ?
808 ACL_MODIFY_MODE_REMOVE : add_mode;
809 ru.rights = rights[i];
810 if (rights[i].global && first_global) {
811 /* first global: reset negative ACLs so local ACLs
812 can't mess things up via them */
813 first_global = FALSE;
814 ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE;
815 }
816 acl_cache_update(aclobj->backend->cache, aclobj->name, &ru);
817 }
818 }
819
acl_object_remove_all_access(struct acl_object * aclobj)820 void acl_object_remove_all_access(struct acl_object *aclobj)
821 {
822 static const char *null = NULL;
823 struct acl_rights rights;
824
825 i_zero(&rights);
826 rights.id_type = ACL_ID_ANYONE;
827 rights.rights = &null;
828 array_push_back(&aclobj->rights, &rights);
829
830 rights.id_type = ACL_ID_OWNER;
831 rights.rights = &null;
832 array_push_back(&aclobj->rights, &rights);
833 }
834
acl_object_add_global_acls(struct acl_object * aclobj)835 void acl_object_add_global_acls(struct acl_object *aclobj)
836 {
837 struct acl_backend *backend = aclobj->backend;
838 const char *vname, *error;
839
840 if (mailbox_list_is_valid_name(backend->list, aclobj->name, &error))
841 vname = mailbox_list_get_vname(backend->list, aclobj->name);
842 else
843 vname = "";
844
845 acl_global_file_get(backend->global_file, vname,
846 aclobj->rights_pool, &aclobj->rights);
847 }
848