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