1 /*
2 Copyright (C) 2004 IC & S dbmail@ic-s.nl
3 Copyright (c) 2004-2012 NFG Net Facilities Group BV support@nfg.nl
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later
9 version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "dbmail.h"
22
23 #define NR_ACL_FLAGS 13 // Need enough space for our 11 real rights and the old RFC 2086 virtual c and d rights
24 #define THIS_MODULE "acl"
25
26 static const char *acl_right_strings[] = {
27 "lookup_flag",
28 "read_flag",
29 "seen_flag",
30 "write_flag",
31 "insert_flag",
32 "post_flag",
33 "create_flag",
34 "delete_flag",
35 "deleted_flag",
36 "expunge_flag",
37 "administer_flag",
38 "create_flag",
39 "delete_flag"
40 };
41
42 static const char acl_right_chars[] = "lrswipkxteacd";
43 //{'l','r','s','w','i','p','k','x','t','e','a','c','d'};
44
45 /* local functions */
46 static ACLRight acl_get_right_from_char(char right_char);
47 static int acl_change_rights(uint64_t userid, uint64_t mboxid,
48 const char *rightsstring, int set);
49 static int acl_replace_rights(uint64_t userid, uint64_t mboxid,
50 const char *rightsstring);
51 static int acl_set_one_right(uint64_t userid, uint64_t mboxid,
52 ACLRight right, int set);
53 static int acl_get_rightsstring_identifier(char *identifier, uint64_t mboxid,
54 /*@out@*/ char *rightsstring);
55 static int acl_get_rightsstring(uint64_t userid, uint64_t mboxid,
56 /*@out@*/ char *rightsstring);
57
58
acl_has_right(MailboxState_T S,uint64_t userid,ACLRight right)59 int acl_has_right(MailboxState_T S, uint64_t userid, ACLRight right)
60 {
61 uint64_t anyone_userid;
62 int test;
63
64 switch(right) {
65 case ACL_RIGHT_SEEN:
66 case ACL_RIGHT_WRITE:
67 case ACL_RIGHT_INSERT:
68 case ACL_RIGHT_POST:
69 case ACL_RIGHT_CREATE:
70 case ACL_RIGHT_DELETE:
71 case ACL_RIGHT_DELETED:
72 case ACL_RIGHT_EXPUNGE:
73 case ACL_RIGHT_ADMINISTER:
74
75 if (MailboxState_getPermission(S) != IMAPPERM_READWRITE)
76 return FALSE;
77 break;
78
79 case ACL_RIGHT_LOOKUP:
80 case ACL_RIGHT_READ:
81 case ACL_RIGHT_NONE:
82 /* Write access not required;
83 * check these by flags. */
84 break;
85 }
86
87 const char *right_flag = acl_right_strings[right];
88
89 /* Check if the user has the right; this will also
90 * return true if the user is the mailbox owner. */
91 if ((test = MailboxState_hasPermission(S, userid, right_flag)))
92 return TRUE;
93
94 /* else check the 'anyone' user */
95 if (auth_user_exists(DBMAIL_ACL_ANYONE_USER, &anyone_userid))
96 return MailboxState_hasPermission(S, anyone_userid, right_flag);
97
98 return FALSE;
99 }
100
acl_set_rights(uint64_t userid,uint64_t mboxid,const char * rightsstring)101 int acl_set_rights(uint64_t userid, uint64_t mboxid, const char *rightsstring)
102 {
103 if (rightsstring[0] == '-')
104 return acl_change_rights(userid, mboxid, rightsstring, 0);
105 if (rightsstring[0] == '+')
106 return acl_change_rights(userid, mboxid, rightsstring, 1);
107 return acl_replace_rights(userid, mboxid, rightsstring);
108 }
109
acl_get_right_from_char(char right_char)110 ACLRight acl_get_right_from_char(char right_char)
111 {
112 switch (right_char) {
113 case 'l':
114 return ACL_RIGHT_LOOKUP;
115 case 'r':
116 return ACL_RIGHT_READ;
117 case 's':
118 return ACL_RIGHT_SEEN;
119 case 'w':
120 return ACL_RIGHT_WRITE;
121 case 'i':
122 return ACL_RIGHT_INSERT;
123 case 'p':
124 return ACL_RIGHT_POST;
125 case 'k':
126 return ACL_RIGHT_CREATE;
127 case 'x':
128 return ACL_RIGHT_DELETE;
129 case 't':
130 return ACL_RIGHT_DELETED;
131 case 'e':
132 return ACL_RIGHT_EXPUNGE;
133 case 'a':
134 return ACL_RIGHT_ADMINISTER;
135 default:
136 TRACE(TRACE_ERR, "error wrong acl character. This error should have been caught earlier!");
137 return ACL_RIGHT_NONE;
138 }
139 }
140
141 int
acl_change_rights(uint64_t userid,uint64_t mboxid,const char * rightsstring,int set)142 acl_change_rights(uint64_t userid, uint64_t mboxid, const char *rightsstring,
143 int set)
144 {
145 size_t i;
146 char rightchar;
147
148 for (i = 1; i < strlen(rightsstring); i++) {
149 rightchar = rightsstring[i];
150 switch (rightchar) {
151 case 'c': // Old RFC 2086 - maps to k in RFC 4314
152 if (acl_set_one_right(userid, mboxid, acl_get_right_from_char('k'), set) < 0) return -1;
153 break;
154 case 'd': // Old RFC 2086 - maps to x, t, and e in RFC 4314
155 if (acl_set_one_right(userid, mboxid, acl_get_right_from_char('x'), set) < 0) return -1;
156 if (acl_set_one_right(userid, mboxid, acl_get_right_from_char('t'), set) < 0) return -1;
157 if (acl_set_one_right(userid, mboxid, acl_get_right_from_char('e'), set) < 0) return -1;
158 break;
159 default:
160 if (acl_set_one_right(userid, mboxid, acl_get_right_from_char(rightchar), set) < 0) return -1;
161 break;
162 }
163 }
164 return 1;
165 }
166
167 int
acl_replace_rights(uint64_t userid,uint64_t mboxid,const char * rights)168 acl_replace_rights(uint64_t userid, uint64_t mboxid, const char *rights)
169 {
170 unsigned i;
171 int set;
172 gchar *rightsstring = NULL;
173
174 rightsstring = g_strndup(rights, 256);
175
176 TRACE(TRACE_DEBUG, "replacing rights for user [%" PRIu64 "], mailbox [%" PRIu64 "] to %s", userid, mboxid, rightsstring);
177
178 // RFC 2086 to RFC 4314 mapping
179 if (strchr(rightsstring, (int) 'c'))
180 rightsstring = g_strconcat(rightsstring, "k\0", NULL);
181 if (strchr(rightsstring, (int) 'd'))
182 rightsstring = g_strconcat(rightsstring, "xte\0", NULL);
183
184 for (i = ACL_RIGHT_LOOKUP; i < ACL_RIGHT_NONE; i++) {
185
186 if (strchr(rightsstring, (int) acl_right_chars[i]))
187 set = 1;
188 else
189 set = 0;
190 if (db_acl_set_right(userid, mboxid, acl_right_strings[i], set) < 0) {
191 TRACE(TRACE_ERR, "error replacing ACL");
192 g_free(rightsstring);
193 return -1;
194 }
195 }
196 g_free(rightsstring);
197 return 1;
198
199 }
200
201 int
acl_set_one_right(uint64_t userid,uint64_t mboxid,ACLRight right,int set)202 acl_set_one_right(uint64_t userid, uint64_t mboxid, ACLRight right, int set)
203 {
204 return db_acl_set_right(userid, mboxid, acl_right_strings[right],
205 set);
206 }
207
208
209 /*
210 int acl_delete_acl(uint64_t userid, uint64_t mboxid)
211 {
212 return db_acl_delete_acl(userid, mboxid);
213 }
214 */
215
acl_get_acl(uint64_t mboxid)216 char *acl_get_acl(uint64_t mboxid)
217 {
218 uint64_t userid;
219 char *username;
220 size_t acl_string_size = 0;
221 size_t acl_strlen;
222 char *acl_string; /* return string */
223 char *identifier; /* one identifier */
224 char *identifier_astring; /* identifier as IMAP astring */
225 char rightsstring[NR_ACL_FLAGS + 1];
226 int result;
227 GList *identifier_list = NULL;
228 unsigned nr_identifiers = 0;
229
230 result = db_acl_get_identifier(mboxid, &identifier_list);
231
232 if (result < 0) {
233 TRACE(TRACE_ERR, "error when getting identifier list for mailbox [%" PRIu64 "].", mboxid);
234 g_list_destroy(identifier_list);
235 return NULL;
236 }
237
238 /* add the current user to the list if this user is the owner
239 * of the mailbox
240 */
241 if (db_get_mailbox_owner(mboxid, &userid) < 0) {
242 TRACE(TRACE_ERR, "error querying ownership of mailbox");
243 g_list_destroy(identifier_list);
244 return NULL;
245 }
246
247 if ((username = auth_get_userid(userid)) == NULL) {
248 TRACE(TRACE_ERR, "error getting username for user [%" PRIu64 "]", userid);
249 g_list_destroy(identifier_list);
250 return NULL;
251 }
252
253 identifier_list = g_list_append(identifier_list, username);
254
255 TRACE(TRACE_DEBUG, "before looping identifiers!");
256
257 identifier_list = g_list_first(identifier_list);
258 while (identifier_list) {
259 nr_identifiers++;
260 identifier_astring = dbmail_imap_astring_as_string((const char *)identifier_list->data);
261 acl_string_size += strlen(identifier_astring) + NR_ACL_FLAGS + 2;
262 g_free(identifier_astring);
263
264 if (! g_list_next(identifier_list))
265 break;
266 identifier_list = g_list_next(identifier_list);
267 }
268
269 TRACE(TRACE_DEBUG, "acl_string size = %zd", acl_string_size);
270
271 acl_string = g_new0(char, acl_string_size + 1);
272
273 identifier_list = g_list_first(identifier_list);
274
275 while (identifier_list) {
276 identifier = (char *) identifier_list->data;
277 if (acl_get_rightsstring_identifier(identifier, mboxid, rightsstring) < 0) {
278 g_list_destroy(identifier_list);
279 g_free(acl_string);
280 return NULL;
281 }
282 TRACE(TRACE_DEBUG, "%s", rightsstring);
283 if (strlen(rightsstring) > 0) {
284 acl_strlen = strlen(acl_string);
285 identifier_astring = dbmail_imap_astring_as_string(identifier);
286 (void) snprintf(&acl_string[acl_strlen], acl_string_size - acl_strlen, "%s %s ", identifier_astring, rightsstring);
287 g_free(identifier_astring);
288 }
289
290 if (! g_list_next(identifier_list))
291 break;
292 identifier_list = g_list_next(identifier_list);
293 }
294 g_list_destroy(identifier_list);
295
296 return g_strstrip(acl_string);
297 }
298
acl_listrights(uint64_t userid,uint64_t mboxid)299 const char *acl_listrights(uint64_t userid, uint64_t mboxid)
300 {
301 int result;
302
303 if ((result = db_user_is_mailbox_owner(userid, mboxid)) < 0) {
304 TRACE(TRACE_ERR, "error checking if user is owner of a mailbox");
305 return NULL;
306 }
307
308 if (result == 0) {
309 /* user is not owner. User will never be granted any right
310 by default, but may be granted any right by setting the
311 right ACL */
312 return "\"\" l r s w i p k x t e a c d";
313 }
314
315 /* user is owner, User will always be granted all rights */
316 return acl_right_chars;
317 }
318
acl_myrights(uint64_t userid,uint64_t mboxid)319 char *acl_myrights(uint64_t userid, uint64_t mboxid)
320 {
321 char *rightsstring;
322
323 if (! (rightsstring = g_new0(char, NR_ACL_FLAGS + 1))) {
324 TRACE(TRACE_ERR, "error allocating memory for rightsstring");
325 return NULL;
326 }
327
328 if (acl_get_rightsstring(userid, mboxid, rightsstring) < 0) {
329 TRACE(TRACE_ERR, "error getting rightsstring.");
330 g_free(rightsstring);
331 return NULL;
332 }
333
334 return rightsstring;
335 }
336
337
acl_get_rightsstring_identifier(char * identifier,uint64_t mboxid,char * rightsstring)338 int acl_get_rightsstring_identifier(char *identifier, uint64_t mboxid, char *rightsstring)
339 {
340 uint64_t userid;
341
342 assert(rightsstring);
343 memset(rightsstring, '\0', NR_ACL_FLAGS + 1);
344
345 if (! auth_user_exists(identifier, &userid)) {
346 TRACE(TRACE_ERR, "error finding user id for user with name [%s]", identifier);
347 return -1;
348 }
349
350 return acl_get_rightsstring(userid, mboxid, rightsstring);
351 }
352
acl_get_rightsstring(uint64_t userid,uint64_t mboxid,char * rightsstring)353 int acl_get_rightsstring(uint64_t userid, uint64_t mboxid, char *rightsstring)
354 {
355 int result;
356 uint64_t owner_idnr;
357 MailboxState_T S;
358 struct ACLMap map;
359
360 assert(rightsstring);
361 memset(rightsstring, '\0', NR_ACL_FLAGS + 1);
362
363 if ((result = db_get_mailbox_owner(mboxid, &owner_idnr)) <= 0)
364 return result;
365
366 if (owner_idnr == userid) {
367 TRACE(TRACE_DEBUG, "mailbox [%" PRIu64 "] is owned by user [%" PRIu64 "], giving all rights", mboxid, userid);
368 g_strlcat(rightsstring, acl_right_chars, NR_ACL_FLAGS+1);
369 return 1;
370 }
371
372 memset(&map, '\0', sizeof(struct ACLMap));
373 S = MailboxState_new(NULL, mboxid);
374 MailboxState_setOwner(S, owner_idnr);
375 result = MailboxState_getAcl(S, userid, &map);
376 MailboxState_free(&S);
377
378 if (result == DM_EQUERY) return result;
379
380 if (map.lookup_flag)
381 g_strlcat(rightsstring,"l", NR_ACL_FLAGS+1);
382 if (map.read_flag)
383 g_strlcat(rightsstring,"r", NR_ACL_FLAGS+1);
384 if (map.seen_flag)
385 g_strlcat(rightsstring,"s", NR_ACL_FLAGS+1);
386 if (map.write_flag)
387 g_strlcat(rightsstring,"w", NR_ACL_FLAGS+1);
388 if (map.insert_flag)
389 g_strlcat(rightsstring,"i", NR_ACL_FLAGS+1);
390 if (map.post_flag)
391 g_strlcat(rightsstring,"p", NR_ACL_FLAGS+1);
392 if (map.create_flag)
393 g_strlcat(rightsstring,"k", NR_ACL_FLAGS+1);
394 if (map.delete_flag)
395 g_strlcat(rightsstring,"x", NR_ACL_FLAGS+1);
396 if (map.deleted_flag)
397 g_strlcat(rightsstring,"t", NR_ACL_FLAGS+1);
398 if (map.expunge_flag)
399 g_strlcat(rightsstring,"e", NR_ACL_FLAGS+1);
400 if (map.administer_flag)
401 g_strlcat(rightsstring,"a", NR_ACL_FLAGS+1);
402
403 // RFC 4314 backwords compatible RFC 2086 virtual c and d rights
404 if (map.create_flag)
405 g_strlcat(rightsstring,"c", NR_ACL_FLAGS+1);
406 if (map.delete_flag || map.deleted_flag || map.expunge_flag)
407 g_strlcat(rightsstring,"d", NR_ACL_FLAGS+1);
408
409 return 1;
410 }
411