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