1 /*
2 ** Copyright 2003-2012 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #include "maildiraclt.h"
7 #include "maildirmisc.h"
8 #include "maildircreate.h"
9 #include <time.h>
10 #if HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
13 #if HAVE_DIRENT_H
14 #include <dirent.h>
15 #define NAMLEN(dirent) strlen((dirent)->d_name)
16 #else
17 #define dirent direct
18 #define NAMLEN(dirent) (dirent)->d_namlen
19 #if HAVE_SYS_NDIR_H
20 #include <sys/ndir.h>
21 #endif
22 #if HAVE_SYS_DIR_H
23 #include <sys/dir.h>
24 #endif
25 #if HAVE_NDIR_H
26 #include <ndir.h>
27 #endif
28 #endif
29 #include <string.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <assert.h>
34
35
36 int maildir_acl_disabled=0;
37
compar_aclt(const void * a,const void * b)38 static int compar_aclt(const void *a, const void *b)
39 {
40 char ca=*(const char *)a;
41 char cb=*(const char *)b;
42
43 return (int)(unsigned char)ca - (int)(unsigned char)cb;
44 }
45
46 /* Post-op fixup of an aclt: collate, remove dupes. */
47
fixup(maildir_aclt * aclt)48 static void fixup(maildir_aclt *aclt)
49 {
50 char *a, *b;
51
52 qsort(*aclt, strlen(*aclt), 1, compar_aclt);
53
54 for (a=b=*aclt; *a; a++)
55 {
56 if (*a == a[1])
57 continue;
58 if ((int)(unsigned char)*a <= ' ')
59 continue; /* Silently drop bad access rights */
60
61 *b++= *a;
62 }
63 *b=0;
64 }
65
validacl(const char * p)66 static int validacl(const char *p)
67 {
68 while (*p)
69 {
70 if ((int)(unsigned char)*p <= ' ')
71 {
72 errno=EINVAL;
73 return -1;
74 }
75 ++p;
76 }
77
78 return 0;
79 }
80
maildir_aclt_init(maildir_aclt * aclt,const char * initvalue_cstr,const maildir_aclt * initvalue_cpy)81 int maildir_aclt_init(maildir_aclt *aclt,
82 const char *initvalue_cstr,
83 const maildir_aclt *initvalue_cpy)
84 {
85 if (initvalue_cpy)
86 initvalue_cstr= *initvalue_cpy;
87
88 *aclt=NULL;
89
90 if (!initvalue_cstr || !*initvalue_cstr)
91 return 0;
92
93 if (validacl(initvalue_cstr) < 0)
94 return -1;
95
96 if ( (*aclt=strdup(initvalue_cstr)) == NULL)
97 return -1;
98 fixup(aclt);
99 return 0;
100 }
101
102 /* Destroy an aclt after it is no longer used. */
103
maildir_aclt_destroy(maildir_aclt * aclt)104 void maildir_aclt_destroy(maildir_aclt *aclt)
105 {
106 if (*aclt)
107 free(*aclt);
108 }
109
110
111 /* Add or remove access chars. */
112
maildir_aclt_add(maildir_aclt * aclt,const char * add_strs,const maildir_aclt * add_aclt)113 int maildir_aclt_add(maildir_aclt *aclt,
114 const char *add_strs,
115 const maildir_aclt *add_aclt)
116 {
117 if (add_aclt)
118 add_strs= *add_aclt;
119
120 if (!add_strs || !*add_strs)
121 return 0;
122
123 if (validacl(add_strs) < 0)
124 return -1;
125
126 if (*aclt)
127 {
128 char *p=realloc(*aclt, strlen(*aclt)+strlen(add_strs)+1);
129
130 if (!p)
131 return -1;
132 strcat(p, add_strs);
133 *aclt=p;
134
135 }
136 else if ( ((*aclt)=strdup(add_strs)) == NULL)
137 return -1;
138
139 fixup(aclt);
140 return 0;
141 }
142
maildir_aclt_del(maildir_aclt * aclt,const char * del_strs,const maildir_aclt * del_aclt)143 int maildir_aclt_del(maildir_aclt *aclt,
144 const char *del_strs,
145 const maildir_aclt *del_aclt)
146 {
147 char *a, *b;
148
149 if (del_aclt)
150 del_strs= *del_aclt;
151
152 if (!del_strs)
153 return 0;
154
155 if (!*aclt)
156 return 0;
157
158 for (a=b=*aclt; *a; a++)
159 {
160 if (strchr(del_strs, *a))
161 continue;
162 *b++= *a;
163 }
164 *b=0;
165
166 if (**aclt == 0)
167 {
168 free(*aclt);
169 *aclt=NULL;
170 }
171 return 0;
172 }
173
174 /* -------------------------------------------------------------------- */
175
176
maildir_aclt_list_init(maildir_aclt_list * aclt_list)177 void maildir_aclt_list_init(maildir_aclt_list *aclt_list)
178 {
179 aclt_list->head=NULL;
180 aclt_list->tail=NULL;
181 }
182
maildir_aclt_list_destroy(maildir_aclt_list * aclt_list)183 void maildir_aclt_list_destroy(maildir_aclt_list *aclt_list)
184 {
185 struct maildir_aclt_node *p;
186
187 for (p=aclt_list->head; p; )
188 {
189 struct maildir_aclt_node *q=p->next;
190
191 free(p->identifier);
192 maildir_aclt_destroy(&p->acl);
193 free(p);
194 p=q;
195 }
196 maildir_aclt_list_init(aclt_list);
197 }
198
199
200 /* Add an <identifier,acl> pair. Returns 0 on success, -1 on failure */
201
maildir_aclt_list_add(maildir_aclt_list * aclt_list,const char * identifier,const char * aclt_str,maildir_aclt * aclt_cpy)202 int maildir_aclt_list_add(maildir_aclt_list *aclt_list,
203 const char *identifier,
204 const char *aclt_str,
205 maildir_aclt *aclt_cpy)
206 {
207 struct maildir_aclt_node *p;
208 const char *q;
209
210 /* Check for valid identifiers */
211
212 for (q=identifier; *q; q++)
213 if ( (int)(unsigned char)*q <= ' ')
214 {
215 errno=EINVAL;
216 return -1;
217 }
218
219 if (*identifier == 0)
220 {
221 errno=EINVAL;
222 return -1;
223 }
224
225 if (aclt_cpy && *aclt_cpy)
226 aclt_str= *aclt_cpy;
227
228 for (p=aclt_list->head; p; p=p->next)
229 {
230 if (strcmp(p->identifier, identifier) == 0)
231 {
232 maildir_aclt_destroy(&p->acl);
233 return maildir_aclt_init(&p->acl, aclt_str, NULL);
234 }
235 }
236
237 if ((p=malloc(sizeof(*p))) == NULL ||
238 (p->identifier=strdup(identifier)) == NULL)
239 {
240 if (p) free(p);
241 return -1;
242 }
243
244 if (maildir_aclt_init(&p->acl, aclt_str, NULL) < 0)
245 {
246 free(p->identifier);
247 free(p);
248 return -1;
249 }
250
251 p->next=NULL;
252 if ((p->prev=aclt_list->tail) != NULL)
253 p->prev->next=p;
254 else
255 aclt_list->head=p;
256 aclt_list->tail=p;
257 return 0;
258 }
259
260 /*
261 ** Remove 'identifier' from the ACL list.
262 */
263
maildir_aclt_list_del(maildir_aclt_list * aclt_list,const char * identifier)264 int maildir_aclt_list_del(maildir_aclt_list *aclt_list,
265 const char *identifier)
266 {
267 struct maildir_aclt_node *p;
268
269 for (p=aclt_list->head; p; p=p->next)
270 {
271 if (strcmp(p->identifier, identifier) == 0)
272 {
273 if (p->prev)
274 p->prev->next=p->next;
275 else aclt_list->head=p->next;
276
277 if (p->next)
278 p->next->prev=p->prev;
279 else aclt_list->tail=p->prev;
280
281 maildir_aclt_destroy(&p->acl);
282 free(p->identifier);
283 free(p);
284 return 0;
285 }
286 }
287 return 0;
288 }
289
290 /*
291 ** Generic enumeration.
292 */
293
maildir_aclt_list_enum(maildir_aclt_list * aclt_list,int (* cb_func)(const char * identifier,const maildir_aclt * acl,void * cb_arg),void * cb_arg)294 int maildir_aclt_list_enum(maildir_aclt_list *aclt_list,
295 int (*cb_func)(const char *identifier,
296 const maildir_aclt *acl,
297 void *cb_arg),
298 void *cb_arg)
299 {
300 struct maildir_aclt_node *p;
301 int rc;
302
303 for (p=aclt_list->head; p; p=p->next)
304 {
305 rc= (*cb_func)(p->identifier, &p->acl, cb_arg);
306
307 if (rc)
308 return rc;
309 }
310 return 0;
311 }
312
maildir_aclt_list_find(maildir_aclt_list * aclt_list,const char * identifier)313 const maildir_aclt *maildir_aclt_list_find(maildir_aclt_list *aclt_list,
314 const char *identifier)
315 {
316 struct maildir_aclt_node *p;
317
318 for (p=aclt_list->head; p; p=p->next)
319 {
320 if (strcmp(p->identifier, identifier) == 0)
321 return &p->acl;
322 }
323 return NULL;
324 }
325
326 /* ---------------------------------------------------------------------- */
327
328 static int maildir_acl_read_check(maildir_aclt_list *aclt_list,
329 const char *maildir,
330 const char *path);
331
maildir_acl_read(maildir_aclt_list * aclt_list,const char * maildir,const char * path)332 int maildir_acl_read(maildir_aclt_list *aclt_list,
333 const char *maildir,
334 const char *path)
335 {
336 int rc=maildir_acl_read_check(aclt_list, maildir, path);
337 char *p, *q;
338
339 if (rc)
340 maildir_aclt_list_destroy(aclt_list);
341
342 if (rc <= 0)
343 return rc;
344
345 /*
346 ** If the ACL config file for this folder was not found,
347 ** check for the ACL config file for its parent folder.
348 */
349
350 if ((p=strdup(path)) == NULL)
351 return -1;
352
353 strcpy(p, path);
354
355 q=strrchr(p, '.');
356
357 if (!q)
358 {
359 free(p);
360 errno=EIO; /* Should not happen */
361 return -1;
362 }
363
364 *q=0;
365
366 rc=maildir_acl_read(aclt_list, maildir, p);
367 if (rc == 0)
368 {
369 /* Make sure to save the default acl list */
370
371 rc=maildir_acl_write(aclt_list, maildir, path, NULL, NULL);
372 if (rc >= 0) /* Ok if rc=1 */
373 rc=0;
374 if (rc)
375 maildir_aclt_list_destroy(aclt_list);
376 }
377 free(p);
378 return rc;
379 }
380
381 /*
382 ** Attempt to retrieve the ACL set for the specified folder.
383 **
384 ** Returns -1 if error.
385 ** Returns 0 if the ACL was retrieved.
386 ** Returns 1 if the ACL configuration file does not exist.
387 */
388
389 static int maildir_aclt_add_default_admin(maildir_aclt_list *aclt_list);
390
maildir_acl_read_check(maildir_aclt_list * aclt_list,const char * maildir,const char * path)391 static int maildir_acl_read_check(maildir_aclt_list *aclt_list,
392 const char *maildir,
393 const char *path)
394 {
395 char *p, *q;
396 FILE *fp;
397 char buffer[BUFSIZ];
398
399 maildir_aclt_list_init(aclt_list);
400
401 if (!maildir || !*maildir)
402 maildir=".";
403 if (!path || !*path)
404 path=".";
405
406 if (strchr(path, '/') || *path != '.')
407 {
408 errno=EINVAL;
409 return -1;
410 }
411
412 if (maildir_acl_disabled)
413 {
414 if (maildir_aclt_list_add(aclt_list, "owner",
415 ACL_LOOKUP ACL_READ
416 ACL_SEEN ACL_WRITE ACL_INSERT
417 ACL_CREATE
418 ACL_DELETEFOLDER
419 ACL_DELETEMSGS ACL_EXPUNGE,
420 NULL) < 0 ||
421 maildir_aclt_add_default_admin(aclt_list))
422 {
423 maildir_aclt_list_destroy(aclt_list);
424 return -1;
425 }
426 return 0;
427 }
428
429 p=malloc(strlen(maildir)+strlen(path)+2);
430
431 if (!p)
432 return -1;
433
434 strcat(strcat(strcpy(p, maildir), "/"), path);
435
436 q=malloc(strlen(p)+sizeof("/" ACLFILE));
437 if (!q)
438 {
439 free(p);
440 return -1;
441 }
442 fp=fopen(strcat(strcpy(q, p), "/" ACLFILE), "r");
443 free(p);
444 free(q);
445
446 if (fp == NULL)
447 {
448 if (strcmp(path, ".") == 0)
449 {
450 /* INBOX ACL default */
451
452 if (maildir_aclt_list_add(aclt_list, "owner",
453 ACL_ALL, NULL) < 0 ||
454 maildir_aclt_add_default_admin(aclt_list))
455 {
456 return -1;
457 }
458 return 0;
459 }
460
461 q=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR "/") +
462 strlen(path));
463 if (!q)
464 return -1;
465
466 strcat(strcat(strcpy(q, maildir), "/" ACLHIERDIR "/"),
467 path+1);
468
469 fp=fopen(q, "r");
470 free(q);
471 }
472
473 if (!fp && errno != ENOENT)
474 return -1;
475
476 if (!fp)
477 return 1;
478
479 errno=0;
480
481 while (fgets(buffer, sizeof(buffer), fp) != NULL)
482 {
483 char *p=strchr(buffer, '\n');
484
485 if (p) *p=0;
486
487 for (p=buffer; *p; p++)
488 if (*p == ' ')
489 {
490 *p=0;
491 do
492 {
493 ++p;
494 } while (*p && *p == ' ');
495 break;
496 }
497
498 if (maildir_aclt_list_add(aclt_list, buffer, p, NULL) < 0)
499 {
500 if (errno != EINVAL)
501 return -1;
502 /* Sweep crap in the ACL file under the carpet */
503 }
504 }
505 if (ferror(fp))
506 {
507 fclose(fp);
508 return -1;
509 }
510 fclose(fp);
511 if (maildir_aclt_add_default_admin(aclt_list))
512 return -1;
513 return 0;
514 }
515
516 /*
517 ** Add the default ACL permissions to the administrators group.
518 **
519 ** Make sure that the ACL entry for "administrators" includes all
520 ** rights.
521 **
522 ** Make sure that any ACL entries for "-administrators" or
523 ** "-group=administrators" do not have LOOKUP and ADMIN.
524 */
525
maildir_aclt_add_default_admin(maildir_aclt_list * aclt_list)526 static int maildir_aclt_add_default_admin(maildir_aclt_list *aclt_list)
527 {
528 const maildir_aclt *old_acl;
529
530 static const char * const drop_acls[]={"-administrators",
531 "-group=administrators"};
532 size_t i;
533
534 if ((old_acl=maildir_aclt_list_find(aclt_list, "group=administrators"))
535 != NULL)
536 {
537 maildir_aclt new_acl;
538
539 if (maildir_aclt_init(&new_acl, ACL_ALL, NULL))
540 return -1;
541
542 if (maildir_aclt_add(&new_acl, NULL, old_acl) ||
543 maildir_aclt_list_add(aclt_list, "group=administrators",
544 NULL, &new_acl))
545 {
546 maildir_aclt_destroy(&new_acl);
547 return -1;
548 }
549 maildir_aclt_destroy(&new_acl);
550 }
551 else
552 {
553 maildir_aclt new_acl;
554
555 old_acl=maildir_aclt_list_find(aclt_list, "administrators");
556
557 if (maildir_aclt_init(&new_acl, ACL_ALL, NULL))
558 return -1;
559
560 if (maildir_aclt_add(&new_acl, NULL, old_acl) ||
561 maildir_aclt_list_add(aclt_list, "administrators",
562 NULL, &new_acl))
563 {
564 maildir_aclt_destroy(&new_acl);
565 return -1;
566 }
567 maildir_aclt_destroy(&new_acl);
568 }
569
570 for (i=0; i<2; i++)
571 {
572 const char *n=drop_acls[i];
573 if (maildir_aclt_list_del(aclt_list, n) < 0)
574 return -1;
575 }
576
577 return 0;
578 }
579
maildir_acl_delete(const char * maildir,const char * path)580 int maildir_acl_delete(const char *maildir,
581 const char *path)
582 {
583 char *p, *q;
584
585 #if 0
586 if (strcmp(path, SHARED) == 0)
587 return 0;
588
589 if (strncmp(path, SHARED ".", sizeof(SHARED)) == 0)
590 return 0;
591 #endif
592 if (!maildir || !*maildir)
593 maildir=".";
594 if (!path || !*path)
595 path=".";
596
597 if (strchr(path, '/') || *path != '.')
598 {
599 errno=EINVAL;
600 return -1;
601 }
602
603 p=malloc(strlen(maildir)+strlen(path)+2);
604
605 if (!p)
606 return -1;
607
608 strcat(strcat(strcpy(p, maildir), "/"), path);
609
610 q=malloc(strlen(p)+sizeof("/" ACLFILE));
611 if (!q)
612 {
613 free(p);
614 return -1;
615 }
616
617 unlink(strcat(strcpy(q, p), "/" ACLFILE));
618 free(p);
619 free(q);
620
621 if (strcmp(path, ".") == 0)
622 {
623 /* INBOX ACL default */
624
625 return 0;
626 }
627
628 q=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR "/") +
629 strlen(path));
630 if (!q)
631 {
632 return -1;
633 }
634 strcat(strcat(strcpy(q, maildir), "/" ACLHIERDIR "/"),
635 path+1);
636
637 unlink(q);
638 free(q);
639 return 0;
640 }
641
642 static int save_acl(const char *identifier, const maildir_aclt *acl,
643 void *cb_arg);
644
645
is_owner(const char * isme,void * void_arg)646 static int is_owner(const char *isme, void *void_arg)
647 {
648 if (void_arg && strcmp(isme, (const char *)void_arg) == 0)
649 return 1;
650
651 return strcmp(isme, "owner") == 0;
652 }
653
is_admin(const char * isme,void * void_arg)654 static int is_admin(const char *isme, void *void_arg)
655 {
656 return strcmp(isme, "administrators") == 0;
657
658 /* We don't need to check for group=administrators, see chk_admin() */
659 }
660
check_adminrights(maildir_aclt * list)661 static int check_adminrights(maildir_aclt *list)
662 {
663 if (strchr(maildir_aclt_ascstr(list), ACL_LOOKUP[0]) == NULL ||
664 strchr(maildir_aclt_ascstr(list), ACL_ADMINISTER[0]) == NULL)
665 {
666 maildir_aclt_destroy(list);
667 return -1;
668 }
669
670 maildir_aclt_destroy(list);
671 return 0;
672 }
673
check_allrights(maildir_aclt * list)674 static int check_allrights(maildir_aclt *list)
675 {
676 const char *all=ACL_ALL;
677
678 while (*all)
679 {
680 if (strchr(maildir_aclt_ascstr(list), *all) == NULL)
681 {
682 maildir_aclt_destroy(list);
683 return -1;
684 }
685 ++all;
686 }
687
688 maildir_aclt_destroy(list);
689 return 0;
690 }
691
692 static int maildir_acl_compute_chkowner(maildir_aclt *aclt,
693 maildir_aclt_list *aclt_list,
694 int (*cb_func)(const char *isme,
695 void *void_arg),
696 void *void_arg,
697 int chkowner);
698
maildir_acl_write(maildir_aclt_list * aclt_list,const char * maildir,const char * path,const char * owner,const char ** err_failedrights)699 int maildir_acl_write(maildir_aclt_list *aclt_list,
700 const char *maildir,
701 const char *path,
702
703 const char *owner,
704 const char **err_failedrights)
705 {
706 int trycreate;
707 struct maildir_tmpcreate_info tci;
708 FILE *fp;
709 char *p, *q;
710 const char *dummy_string;
711 maildir_aclt chkacls;
712
713 if (!err_failedrights)
714 err_failedrights= &dummy_string;
715
716 if (!maildir || !*maildir)
717 maildir=".";
718 if (!path || !*path)
719 path=".";
720
721 if (strchr(path, '/') || *path != '.')
722 {
723 errno=EINVAL;
724 return -1;
725 }
726
727 if (strcmp(path, ".")) /* Sanity check */
728 for (dummy_string=path; *dummy_string; dummy_string++)
729 if (*dummy_string == '.' &&
730 (dummy_string[1] == '.' ||
731 dummy_string[1] == 0))
732 {
733 errno=EINVAL;
734 return -1;
735 }
736
737
738 if (maildir_acl_compute_chkowner(&chkacls, aclt_list, is_owner, NULL,
739 0))
740 {
741 maildir_aclt_destroy(&chkacls);
742 errno=EINVAL;
743 return -1;
744 }
745
746 if (check_adminrights(&chkacls))
747 {
748 *err_failedrights="owner";
749 errno=EINVAL;
750 return -1;
751 }
752
753 if (owner)
754 {
755 if (maildir_acl_compute_chkowner(&chkacls, aclt_list, is_owner,
756 (void *)owner, 0))
757 {
758 maildir_aclt_destroy(&chkacls);
759 errno=EINVAL;
760 return -1;
761 }
762 if (check_adminrights(&chkacls))
763 {
764 *err_failedrights=owner;
765 errno=EINVAL;
766 return -1;
767 }
768 }
769
770 if (maildir_acl_compute(&chkacls, aclt_list, is_admin, NULL))
771 {
772 maildir_aclt_destroy(&chkacls);
773 errno=EINVAL;
774 return -1;
775 }
776 if (check_allrights(&chkacls))
777 {
778 errno=EINVAL;
779 return -1;
780 }
781
782 p=malloc(strlen(maildir)+strlen(path)+2);
783
784 if (!p)
785 return -1;
786
787 strcat(strcat(strcpy(p, maildir), "/"), path);
788
789 maildir_tmpcreate_init(&tci);
790
791 tci.maildir=p;
792 tci.uniq="acl";
793 tci.doordie=1;
794
795 fp=maildir_tmpcreate_fp(&tci);
796
797 trycreate=0;
798
799 if (fp)
800 {
801 q=malloc(strlen(p) + sizeof("/" ACLFILE));
802 if (!q)
803 {
804 fclose(fp);
805 unlink(tci.tmpname);
806 maildir_tmpcreate_free(&tci);
807 free(p);
808 return -1;
809 }
810 strcat(strcpy(q, p), "/" ACLFILE);
811 free(tci.newname);
812 tci.newname=q;
813 free(p);
814 }
815 else
816 {
817 free(p);
818
819 q=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR "/") +
820 strlen(path));
821 if (!q)
822 {
823 maildir_tmpcreate_free(&tci);
824 return -1;
825 }
826 strcat(strcat(strcpy(q, maildir), "/" ACLHIERDIR "/"), path+1);
827
828 tci.maildir=maildir;
829 tci.uniq="acl";
830 tci.doordie=1;
831
832 fp=maildir_tmpcreate_fp(&tci);
833
834 if (!fp)
835 {
836 free(q);
837 maildir_tmpcreate_free(&tci);
838 return -1;
839 }
840 free(tci.newname);
841 tci.newname=q;
842 trycreate=1;
843 }
844
845 if (maildir_aclt_list_enum(aclt_list, save_acl, fp) < 0 ||
846 ferror(fp) || fflush(fp) < 0)
847 {
848 fclose(fp);
849 unlink(tci.tmpname);
850 maildir_tmpcreate_free(&tci);
851 return -1;
852 }
853 fclose(fp);
854
855 if (rename(tci.tmpname, tci.newname) < 0)
856 {
857 /* Perhaps ACLHIERDIR needs to be created? */
858
859 if (!trycreate)
860 {
861 unlink(tci.tmpname);
862 maildir_tmpcreate_free(&tci);
863 return -1;
864 }
865
866 p=strrchr(tci.newname, '/');
867 *p=0;
868 mkdir(tci.newname, 0755);
869 *p='/';
870
871 if (rename(tci.tmpname, tci.newname) < 0)
872 {
873 unlink(tci.tmpname);
874 maildir_tmpcreate_free(&tci);
875 return -1;
876 }
877 }
878 maildir_tmpcreate_free(&tci);
879 return 0;
880 }
881
save_acl(const char * identifier,const maildir_aclt * acl,void * cb_arg)882 static int save_acl(const char *identifier, const maildir_aclt *acl,
883 void *cb_arg)
884 {
885 if (fprintf((FILE *)cb_arg, "%s %s\n",
886 identifier,
887 maildir_aclt_ascstr(acl)) < 0)
888 return -1;
889 return 0;
890 }
891
892 struct maildir_acl_resetList {
893 struct maildir_acl_resetList *next;
894 char *mbox;
895 };
896
897 /*
898 ** When a maildir is opened check for stale entries in Maildir/ACLHIERDIR.
899 **
900 ** Maildir/ACLHIERDIR/folder.subfolder should be removed unless there exists
901 ** Maildir/.folder.subfolder.subsubfolder
902 **
903 **
904 ** acl_check_cb is the callback function for maildir_list, which receives
905 ** INBOX.folder.subfolder.subsubfolder. It goes through the link list with
906 ** Maildir/ACLHIERDIR's contents, and removes folder.subfolder if its found.
907 **
908 ** After maildir_list is done, anything that's left in the list can be safely
909 ** removed.
910 */
911
acl_check_cb(const char * mbox,void * voidarg)912 static void acl_check_cb(const char *mbox, void *voidarg)
913 {
914 struct maildir_acl_resetList **l=
915 (struct maildir_acl_resetList **)voidarg;
916
917 if (strncmp(mbox, INBOX ".", sizeof(INBOX ".")-1))
918 return; /* Huh? */
919
920 mbox += sizeof(INBOX ".")-1;
921
922 while (*l)
923 {
924 int cl= strlen( (*l)->mbox );
925
926 if (strncmp(mbox, (*l)->mbox, cl) == 0 &&
927 mbox[cl] == '.')
928 {
929 struct maildir_acl_resetList *p= *l;
930
931 *l= p->next;
932 free(p->mbox);
933 free(p);
934 continue;
935 }
936
937 l= &(*l)->next;
938 }
939 }
940
maildir_acl_reset(const char * maildir)941 int maildir_acl_reset(const char *maildir)
942 {
943 DIR *dirp;
944 struct dirent *de;
945 char *p;
946 struct maildir_acl_resetList *rl=NULL;
947 struct maildir_acl_resetList *r;
948 time_t now;
949 struct stat stat_buf;
950
951 p=malloc(strlen(maildir) + sizeof("/" ACLHIERDIR));
952 if (!p)
953 return -1;
954
955 strcat(strcpy(p, maildir), "/" ACLHIERDIR);
956
957 dirp=opendir(p);
958
959 if (!dirp)
960 {
961 mkdir(p, 0755);
962 dirp=opendir(p);
963 }
964 free(p);
965
966 while (dirp && (de=readdir(dirp)) != NULL)
967 {
968 if (de->d_name[0] == '.')
969 continue;
970
971 if ((r=malloc(sizeof(struct maildir_acl_resetList))) == NULL
972 || (r->mbox=strdup(de->d_name)) == NULL)
973 {
974 if (r)
975 free(r);
976
977 while (rl)
978 {
979 r=rl;
980 rl=r->next;
981 free(r->mbox);
982 free(r);
983 }
984 closedir(dirp);
985 return -1;
986 }
987
988 r->next=rl;
989 rl=r;
990 }
991 if (dirp) closedir(dirp);
992
993 maildir_list(maildir, acl_check_cb, &rl);
994
995 time(&now);
996
997 while (rl)
998 {
999 r=rl;
1000 rl=r->next;
1001
1002 p=malloc(strlen(maildir)+strlen(r->mbox) +
1003 sizeof("/" ACLHIERDIR "/"));
1004 if (p)
1005 {
1006 strcat(strcat(strcpy(p, maildir),
1007 "/" ACLHIERDIR "/"), r->mbox);
1008
1009 /* Only unlink stale entries after 1 hour (race) */
1010
1011 if (stat(p, &stat_buf) == 0 &&
1012 stat_buf.st_mtime < now - 60*60)
1013 unlink(p);
1014 free(p);
1015 }
1016 free(r->mbox);
1017 free(r);
1018 }
1019 return 0;
1020 }
1021
1022 /*
1023 ** An ACL entry for "administrators" or "group=administrators" will match
1024 ** either one.
1025 */
1026
chk_admin(int (* cb_func)(const char * isme,void * void_arg),const char * identifier,void * void_arg)1027 static int chk_admin(int (*cb_func)(const char *isme,
1028 void *void_arg),
1029 const char *identifier,
1030 void *void_arg)
1031 {
1032 if (strcmp(identifier, "administrators") == 0 ||
1033 strcmp(identifier, "group=administrators") == 0)
1034 {
1035 int rc=(*cb_func)("administrators", void_arg);
1036
1037 if (rc == 0)
1038 rc=(*cb_func)("group=administrators", void_arg);
1039 return rc;
1040 }
1041
1042 return (*cb_func)(identifier, void_arg);
1043 }
1044
1045 #define ISIDENT(s) \
1046 (MAILDIR_ACL_ANYONE(s) ? 1: chk_admin(cb_func, (s), void_arg))
1047
maildir_acl_compute_chkowner(maildir_aclt * aclt,maildir_aclt_list * aclt_list,int (* cb_func)(const char * isme,void * void_arg),void * void_arg,int chkowner)1048 static int maildir_acl_compute_chkowner(maildir_aclt *aclt,
1049 maildir_aclt_list *aclt_list,
1050 int (*cb_func)(const char *isme,
1051 void *void_arg),
1052 void *void_arg,
1053 int chkowner)
1054 {
1055 struct maildir_aclt_node *p;
1056 int rc;
1057
1058 if (maildir_aclt_init(aclt, "", NULL) < 0)
1059 return -1;
1060
1061 for (p=aclt_list->head; p; p=p->next)
1062 {
1063 if (p->identifier[0] == '-')
1064 continue;
1065
1066 rc= ISIDENT(p->identifier);
1067
1068 if (rc < 0)
1069 {
1070 maildir_aclt_destroy(aclt);
1071 return rc;
1072 }
1073
1074 if (rc == 0)
1075 continue;
1076
1077 if (maildir_aclt_add(aclt, NULL, &p->acl) < 0)
1078 {
1079 maildir_aclt_destroy(aclt);
1080 return rc;
1081 }
1082 }
1083
1084 for (p=aclt_list->head; p; p=p->next)
1085 {
1086 if (p->identifier[0] != '-')
1087 continue;
1088
1089 rc= ISIDENT(p->identifier+1);
1090
1091 if (rc < 0)
1092 {
1093 maildir_aclt_destroy(aclt);
1094 return rc;
1095 }
1096
1097 if (rc == 0)
1098 continue;
1099
1100 if (maildir_aclt_del(aclt, NULL, &p->acl) < 0)
1101 {
1102 maildir_aclt_destroy(aclt);
1103 return rc;
1104 }
1105 }
1106
1107 /*
1108 ** In our scheme, the owner always gets admin rights.
1109 */
1110
1111 rc=chkowner ? (*cb_func)("owner", void_arg):0;
1112
1113 if (maildir_acl_disabled)
1114 rc=0; /* Except when ACLs are disabled */
1115
1116 if (rc < 0)
1117 {
1118 maildir_aclt_destroy(aclt);
1119 return rc;
1120 }
1121
1122 if (rc)
1123 {
1124 if (maildir_aclt_add(aclt, ACL_ADMINISTER, NULL) < 0)
1125 {
1126 maildir_aclt_destroy(aclt);
1127 return rc;
1128 }
1129 }
1130 return 0;
1131 }
1132
maildir_acl_compute(maildir_aclt * aclt,maildir_aclt_list * aclt_list,int (* cb_func)(const char * isme,void * void_arg),void * void_arg)1133 int maildir_acl_compute(maildir_aclt *aclt, maildir_aclt_list *aclt_list,
1134 int (*cb_func)(const char *isme,
1135 void *void_arg), void *void_arg)
1136 {
1137 return maildir_acl_compute_chkowner(aclt, aclt_list, cb_func, void_arg,
1138 1);
1139 }
1140
1141 static int chk_array(const char *identifier, void *void_arg);
1142
maildir_acl_compute_array(maildir_aclt * aclt,maildir_aclt_list * aclt_list,const char * const * identifiers)1143 int maildir_acl_compute_array(maildir_aclt *aclt,
1144 maildir_aclt_list *aclt_list,
1145 const char * const *identifiers)
1146 {
1147 return maildir_acl_compute(aclt, aclt_list, chk_array,
1148 (void *)identifiers);
1149 }
1150
chk_array(const char * identifier,void * void_arg)1151 static int chk_array(const char *identifier, void *void_arg)
1152 {
1153 const char * const *p=(const char * const *)void_arg;
1154 size_t i;
1155
1156 for (i=0; p[i]; i++)
1157 if (strcmp(identifier, p[i]) == 0)
1158 return 1;
1159 return 0;
1160 }
1161
1162 /* -------------------------------------------------------------------- */
1163
maildir_acl_canlistrights(const char * myrights)1164 int maildir_acl_canlistrights(const char *myrights)
1165 {
1166 return (strchr(myrights, ACL_LOOKUP[0]) ||
1167 strchr(myrights, ACL_READ[0]) ||
1168 strchr(myrights, ACL_INSERT[0]) ||
1169 strchr(myrights, ACL_CREATE[0]) ||
1170 strchr(myrights, ACL_DELETEFOLDER[0]) ||
1171 strchr(myrights, ACL_EXPUNGE[0]) ||
1172 strchr(myrights, ACL_ADMINISTER[0]));
1173 }
1174
1175 /* --------------------------------------------------------------------- */
1176
1177 /*
1178 ** Compute owner ACL identifiers applicable to a mailbox that's owned by
1179 ** 'mailbox_owner'.
1180 */
1181
get_owner_list(int (* cb_func)(const char *,void *),const char * c,const char * mailbox_owner,void * arg)1182 static int get_owner_list( int (*cb_func)(const char *, void *),
1183 const char *c,
1184 const char *mailbox_owner, void *arg)
1185 {
1186 char *a;
1187 int rc;
1188 const char *p, *q;
1189
1190 a=malloc(sizeof("user=")+strlen(c));
1191 if (!a)
1192 return -1;
1193
1194 strcat(strcpy(a, "user="), c);
1195
1196 rc=(*cb_func)(a, arg);
1197
1198 if (rc == 0 && strcmp(a, mailbox_owner) == 0)
1199 rc=(*cb_func)("owner", arg);
1200 free(a);
1201
1202 if (rc)
1203 return rc;
1204
1205 c=getenv("OPTIONS");
1206
1207 for (p=c; p && *p; )
1208 {
1209 if (*p == ',')
1210 {
1211 ++p;
1212 continue;
1213 }
1214
1215 q=p;
1216 while (*p && *p != ',')
1217 ++p;
1218
1219 if (strncmp(q, "group=", 6) == 0)
1220 {
1221 a=malloc(p-q+1);
1222 if (!a)
1223 return -1;
1224
1225 memcpy(a, q, p-q);
1226 a[p-q]=0;
1227 rc=(*cb_func)(a, arg);
1228 free(a);
1229 if (rc)
1230 return rc;
1231 }
1232 }
1233 return 0;
1234 }
1235
count_owner_list(const char * o,void * arg)1236 static int count_owner_list(const char *o, void *arg)
1237 {
1238 ++*(size_t *)arg;
1239
1240 return 0;
1241 }
1242
save_owner_list(const char * o,void * arg)1243 static int save_owner_list(const char *o, void *arg)
1244 {
1245 char ***p=(char ***)arg;
1246
1247 **p=strdup(o);
1248
1249 if (**p == NULL)
1250 return -1;
1251
1252 ++*p;
1253 return 0;
1254 }
1255
maildir_acl_computerights(maildir_aclt * rights,maildir_aclt_list * acl_list,const char * me,const char * folder_owner)1256 int maildir_acl_computerights(maildir_aclt *rights,
1257 maildir_aclt_list *acl_list,
1258 const char *me,
1259 const char *folder_owner)
1260 {
1261 char **owner_array;
1262 size_t owner_cnt;
1263 char **p;
1264 int rc;
1265
1266 owner_cnt=1;
1267
1268 if (get_owner_list(count_owner_list, me, folder_owner,
1269 (void *)&owner_cnt) ||
1270 (owner_array=calloc(owner_cnt, sizeof(char *))) == NULL)
1271 return -1;
1272
1273 p=owner_array;
1274
1275 if (get_owner_list(save_owner_list, me, folder_owner, (void *)&p))
1276 {
1277 for (owner_cnt=0; owner_array[owner_cnt]; ++owner_cnt)
1278 free(owner_array[owner_cnt]);
1279 free(owner_array);
1280 return -1;
1281 }
1282
1283 rc=maildir_acl_compute_array(rights, acl_list,
1284 (const char * const *)owner_array);
1285
1286 for (owner_cnt=0; owner_array[owner_cnt]; ++owner_cnt)
1287 free(owner_array[owner_cnt]);
1288 free(owner_array);
1289 return rc;
1290 }
1291