Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / servers / exchange / lib / e2k-security-descriptor.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* Copyright (C) 2001-2004 Novell, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU Lesser General Public
7  * License as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "e2k-security-descriptor.h"
25 #include "e2k-sid.h"
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <libxml/parser.h>
30 #include <libxml/tree.h>
31 #include <libxml/xmlmemory.h>
32
33 struct _E2kSecurityDescriptorPrivate {
34         GByteArray *header;
35         guint16 control_flags;
36         GArray *aces;
37
38         E2kSid *default_sid, *owner, *group;
39         GHashTable *sids, *sid_order;
40 };
41
42 typedef struct {
43         guint8  Revision;
44         guint8  Sbz1;
45         guint16 Control;
46         guint32 Owner;
47         guint32 Group;
48         guint32 Sacl;
49         guint32 Dacl;
50 } E2k_SECURITY_DESCRIPTOR_RELATIVE;
51
52 #define E2K_SECURITY_DESCRIPTOR_REVISION 1
53 #define E2K_SE_DACL_PRESENT              GUINT16_TO_LE(0x0004)
54 #define E2K_SE_SACL_PRESENT              GUINT16_TO_LE(0x0010)
55 #define E2K_SE_DACL_PROTECTED            GUINT16_TO_LE(0x1000)
56
57 typedef struct {
58         guint8  AclRevision;
59         guint8  Sbz1;
60         guint16 AclSize;
61         guint16 AceCount;
62         guint16 Sbz2;
63 } E2k_ACL;
64
65 #define E2K_ACL_REVISION 2
66
67 typedef struct {
68         guint8  AceType;
69         guint8  AceFlags;
70         guint16 AceSize;
71 } E2k_ACE_HEADER;
72
73 #define E2K_ACCESS_ALLOWED_ACE_TYPE (0x00)
74 #define E2K_ACCESS_DENIED_ACE_TYPE  (0x01)
75
76 #define E2K_OBJECT_INHERIT_ACE      (0x01)
77 #define E2K_CONTAINER_INHERIT_ACE   (0x02)
78 #define E2K_INHERIT_ONLY_ACE        (0x08)
79
80 typedef struct {
81         E2k_ACE_HEADER  Header;
82         guint32         Mask;
83         E2kSid         *Sid;
84 } E2k_ACE;
85
86 typedef struct {
87         guint32 mapi_permission;
88         guint32 container_allowed, container_not_denied;
89         guint32 object_allowed, object_not_denied;
90 } E2kPermissionsMap;
91
92 /* The magic numbers are from the WSS SDK, except modified to match
93  * Outlook a bit.
94  */
95 #define LE(x) (GUINT32_TO_LE (x))
96 static E2kPermissionsMap permissions_map[] = {
97         { E2K_PERMISSION_READ_ANY,
98           LE(0x000000), LE(0x000000), LE(0x1208a9), LE(0x0008a9) },
99         { E2K_PERMISSION_CREATE,
100           LE(0x000002), LE(0x000002), LE(0x000000), LE(0x000000) },
101         { E2K_PERMISSION_CREATE_SUBFOLDER,
102           LE(0x000004), LE(0x000004), LE(0x000000), LE(0x000000) },
103         { E2K_PERMISSION_EDIT_OWNED,
104           LE(0x000000), LE(0x000000), LE(0x000200), LE(0x000000) },
105         { E2K_PERMISSION_DELETE_OWNED,
106           LE(0x000000), LE(0x000000), LE(0x000400), LE(0x000000) },
107         { E2K_PERMISSION_EDIT_ANY,
108           LE(0x000000), LE(0x000000), LE(0x0c0116), LE(0x1e0316) },
109         { E2K_PERMISSION_DELETE_ANY,
110           LE(0x000000), LE(0x000000), LE(0x010000), LE(0x010400) },
111         { E2K_PERMISSION_OWNER,
112           LE(0x0d4110), LE(0x0d4110), LE(0x000000), LE(0x000000) },
113         { E2K_PERMISSION_CONTACT,
114           LE(0x008000), LE(0x008000), LE(0x000000), LE(0x000000) },
115         { E2K_PERMISSION_FOLDER_VISIBLE,
116           LE(0x1208a9), LE(0x1200a9), LE(0x000000), LE(0x000000) }
117 };
118 static const int permissions_map_size =
119         sizeof (permissions_map) / sizeof (permissions_map[0]);
120
121 static const guint32 container_permissions_all = LE(0x1fc9bf);
122 static const guint32 object_permissions_all    = LE(0x1f0fbf);
123 #undef LE
124
125 #define PARENT_TYPE G_TYPE_OBJECT
126 static GObjectClass *parent_class = NULL;
127
128 static void dispose (GObject *object);
129
130 static void
131 class_init (GObjectClass *object_class)
132 {
133         parent_class = g_type_class_ref (PARENT_TYPE);
134
135         object_class->dispose = dispose;
136 }
137
138 static void
139 init (E2kSecurityDescriptor *sd)
140 {
141         sd->priv = g_new0 (E2kSecurityDescriptorPrivate, 1);
142
143         sd->priv->sids = g_hash_table_new (e2k_sid_binary_sid_hash,
144                                            e2k_sid_binary_sid_equal);
145         sd->priv->sid_order = g_hash_table_new (NULL, NULL);
146         sd->priv->aces = g_array_new (FALSE, TRUE, sizeof (E2k_ACE));
147 }
148
149 static void
150 free_sid (gpointer key, gpointer sid, gpointer data)
151 {
152         g_object_unref (sid);
153 }
154
155 static void
156 dispose (GObject *object)
157 {
158         E2kSecurityDescriptor *sd = (E2kSecurityDescriptor *) object;
159
160         if (sd->priv) {
161                 g_hash_table_foreach (sd->priv->sids, free_sid, NULL);
162                 g_hash_table_destroy (sd->priv->sids);
163                 g_hash_table_destroy (sd->priv->sid_order);
164
165                 g_array_free (sd->priv->aces, TRUE);
166
167                 if (sd->priv->header)
168                         g_byte_array_free (sd->priv->header, TRUE);
169
170                 g_free (sd->priv);
171                 sd->priv = NULL;
172         }
173
174         G_OBJECT_CLASS (parent_class)->dispose (object);
175 }
176
177 E2K_MAKE_TYPE (e2k_security_descriptor, E2kSecurityDescriptor, class_init, init, PARENT_TYPE)
178
179
180 /* This determines the relative ordering of any two ACEs in a SID.
181  * See docs/security for details.
182  */
183 static int
184 ace_compar (E2k_ACE *ace1, E2k_ACE *ace2, E2kSecurityDescriptor *sd)
185 {
186         E2kSidType t1;
187         E2kSidType t2;
188         int order1, order2;
189
190         if (ace1 == ace2)
191                 return 0;
192
193         /* Figure out which overall section the SID will go in and
194          * what its order within that group is.
195          */
196         if (ace1->Sid == sd->priv->default_sid)
197                 t1 = E2K_SID_TYPE_GROUP;
198         else
199                 t1 = e2k_sid_get_sid_type (ace1->Sid);
200         order1 = GPOINTER_TO_INT (g_hash_table_lookup (sd->priv->sid_order,
201                                                        ace1->Sid));
202
203         if (ace2->Sid == sd->priv->default_sid)
204                 t2 = E2K_SID_TYPE_GROUP;
205         else
206                 t2 = e2k_sid_get_sid_type (ace2->Sid);
207         order2 = GPOINTER_TO_INT (g_hash_table_lookup (sd->priv->sid_order,
208                                                        ace2->Sid));
209
210         if (t1 != t2) {
211                 if (t1 == E2K_SID_TYPE_USER)
212                         return -1;
213                 else if (t2 == E2K_SID_TYPE_USER)
214                         return 1;
215                 else if (t1 == E2K_SID_TYPE_GROUP)
216                         return 1;
217                 else /* (t2 == E2K_SID_TYPE_GROUP) */
218                         return -1;
219         }
220
221         if (t1 != E2K_SID_TYPE_GROUP) {
222                 /* Object-level ACEs go before Container-level ACEs */
223                 if ((ace1->Header.AceFlags & E2K_OBJECT_INHERIT_ACE) &&
224                     !(ace2->Header.AceFlags & E2K_OBJECT_INHERIT_ACE))
225                         return -1;
226                 else if ((ace2->Header.AceFlags & E2K_OBJECT_INHERIT_ACE) &&
227                          !(ace1->Header.AceFlags & E2K_OBJECT_INHERIT_ACE))
228                         return 1;
229
230                 /* Compare SID order */
231                 if (order1 < order2)
232                         return -1;
233                 else if (order1 > order2)
234                         return 1;
235
236                 /* Allowed ACEs for a given SID go before Denied ACEs */
237                 if (ace1->Header.AceType == ace2->Header.AceType)
238                         return 0;
239                 else if (ace1->Header.AceType == E2K_ACCESS_ALLOWED_ACE_TYPE)
240                         return -1;
241                 else
242                         return 1;
243         } else {
244                 /* For groups, object-level ACEs go after Container-level */
245                 if ((ace1->Header.AceFlags & E2K_OBJECT_INHERIT_ACE) &&
246                     !(ace2->Header.AceFlags & E2K_OBJECT_INHERIT_ACE))
247                         return 1;
248                 else if ((ace2->Header.AceFlags & E2K_OBJECT_INHERIT_ACE) &&
249                          !(ace1->Header.AceFlags & E2K_OBJECT_INHERIT_ACE))
250                         return -1;
251
252                 /* Default comes after groups in each section */
253                 if (ace1->Sid != ace2->Sid) {
254                         if (ace1->Sid == sd->priv->default_sid)
255                                 return 1;
256                         else if (ace2->Sid == sd->priv->default_sid)
257                                 return -1;
258                 }
259
260                 /* All Allowed ACEs go before all Denied ACEs */
261                 if (ace1->Header.AceType == E2K_ACCESS_ALLOWED_ACE_TYPE &&
262                     ace2->Header.AceType == E2K_ACCESS_DENIED_ACE_TYPE)
263                         return -1;
264                 else if (ace1->Header.AceType == E2K_ACCESS_DENIED_ACE_TYPE &&
265                          ace2->Header.AceType == E2K_ACCESS_ALLOWED_ACE_TYPE)
266                         return 1;
267
268                 /* Compare SID order */
269                 if (order1 < order2)
270                         return -1;
271                 else if (order1 > order2)
272                         return 1;
273                 else
274                         return 0;
275         }
276 }
277
278
279 static xmlNode *
280 find_child (xmlNode *node, const char *name)
281 {
282         for (node = node->xmlChildrenNode; node; node = node->next) {
283                 if (node->name && !strcmp (node->name, name))
284                         return node;
285         }
286         return NULL;
287 }
288
289 static void
290 extract_sids (E2kSecurityDescriptor *sd, xmlNodePtr node)
291 {
292         xmlNodePtr string_sid_node, type_node, display_name_node;
293         char *string_sid, *content, *display_name;
294         const guint8 *bsid;
295         E2kSid *sid;
296         E2kSidType type;
297
298         for (; node; node = node->next) {
299                 if (strcmp (node->name, "sid") != 0) {
300                         if (node->xmlChildrenNode)
301                                 extract_sids (sd, node->xmlChildrenNode);
302                         continue;
303                 }
304
305                 string_sid_node = find_child (node, "string_sid");
306                 type_node = find_child (node, "type");
307                 display_name_node = find_child (node, "display_name");
308                 if (!string_sid_node || !type_node)
309                         continue;
310
311                 string_sid = xmlNodeGetContent (string_sid_node);
312
313                 content = xmlNodeGetContent (type_node);
314                 if (!content || !strcmp (content, "user"))
315                         type = E2K_SID_TYPE_USER;
316                 else if (!strcmp (content, "group"))
317                         type = E2K_SID_TYPE_GROUP;
318                 else if (!strcmp (content, "well_known_group"))
319                         type = E2K_SID_TYPE_WELL_KNOWN_GROUP;
320                 else if (!strcmp (content, "alias"))
321                         type = E2K_SID_TYPE_ALIAS;
322                 else
323                         type = E2K_SID_TYPE_INVALID;
324                 xmlFree (content);
325
326                 if (display_name_node)
327                         display_name = xmlNodeGetContent (display_name_node);
328                 else
329                         display_name = NULL;
330
331                 sid = e2k_sid_new_from_string_sid (type, string_sid,
332                                                    display_name);
333                 xmlFree (string_sid);
334                 if (display_name)
335                         xmlFree (display_name);
336
337                 bsid = e2k_sid_get_binary_sid (sid);
338                 if (g_hash_table_lookup (sd->priv->sids, bsid)) {
339                         g_object_unref (sid);
340                         continue;
341                 }
342
343                 g_hash_table_insert (sd->priv->sids, (char *)bsid, sid);
344         }
345 }
346
347 static gboolean
348 parse_sid (E2kSecurityDescriptor *sd, GByteArray *binsd, guint16 *off,
349            E2kSid **sid)
350 {
351         int sid_len;
352
353         if (binsd->len - *off < E2K_SID_BINARY_SID_MIN_LEN)
354                 return FALSE;
355         sid_len = E2K_SID_BINARY_SID_LEN (binsd->data + *off);
356         if (binsd->len - *off < sid_len)
357                 return FALSE;
358
359         *sid = g_hash_table_lookup (sd->priv->sids, binsd->data + *off);
360         *off += sid_len;
361
362         return *sid != NULL;
363 }
364
365 static gboolean
366 parse_acl (E2kSecurityDescriptor *sd, GByteArray *binsd, guint16 *off)
367 {
368         E2k_ACL aclbuf;
369         E2k_ACE acebuf;
370         int ace_count, i;
371
372         if (binsd->len - *off < sizeof (E2k_ACL))
373                 return FALSE;
374
375         memcpy (&aclbuf, binsd->data + *off, sizeof (aclbuf));
376         if (*off + GUINT16_FROM_LE (aclbuf.AclSize) > binsd->len)
377                 return FALSE;
378         if (aclbuf.AclRevision != E2K_ACL_REVISION)
379                 return FALSE;
380
381         ace_count = GUINT16_FROM_LE (aclbuf.AceCount);
382
383         *off += sizeof (aclbuf);
384         for (i = 0; i < ace_count; i++) {
385                 if (binsd->len - *off < sizeof (E2k_ACE))
386                         return FALSE;
387
388                 memcpy (&acebuf, binsd->data + *off,
389                         sizeof (acebuf.Header) + sizeof (acebuf.Mask));
390                 *off += sizeof (acebuf.Header) + sizeof (acebuf.Mask);
391
392                 /* If either of OBJECT_INHERIT_ACE or INHERIT_ONLY_ACE
393                  * is set, both must be.
394                  */
395                 if (acebuf.Header.AceFlags & E2K_OBJECT_INHERIT_ACE) {
396                         if (!(acebuf.Header.AceFlags & E2K_INHERIT_ONLY_ACE))
397                                 return FALSE;
398                 } else {
399                         if (acebuf.Header.AceFlags & E2K_INHERIT_ONLY_ACE)
400                                 return FALSE;
401                 }
402
403                 if (!parse_sid (sd, binsd, off, &acebuf.Sid))
404                         return FALSE;
405
406                 if (!g_hash_table_lookup (sd->priv->sid_order, acebuf.Sid)) {
407                         int size = g_hash_table_size (sd->priv->sid_order);
408
409                         g_hash_table_insert (sd->priv->sid_order, acebuf.Sid,
410                                              GUINT_TO_POINTER (size + 1));
411                 }
412
413                 g_array_append_val (sd->priv->aces, acebuf);
414         }
415
416         return TRUE;
417 }
418
419 /**
420  * e2k_security_descriptor_new:
421  * @xml_form: the XML form of the folder's security descriptor
422  * (The "http://schemas.microsoft.com/exchange/security/descriptor"
423  * property, aka %E2K_PR_EXCHANGE_SD_XML)
424  * @binary_form: the binary form of the folder's security descriptor
425  * (The "http://schemas.microsoft.com/exchange/ntsecuritydescriptor"
426  * property, aka %E2K_PR_EXCHANGE_SD_BINARY)
427  *
428  * Constructs an #E2kSecurityDescriptor from the data in @xml_form and
429  * @binary_form.
430  *
431  * Return value: the security descriptor, or %NULL if the data could
432  * not be parsed.
433  **/
434 E2kSecurityDescriptor *
435 e2k_security_descriptor_new (xmlNodePtr xml_form, GByteArray *binary_form)
436 {
437         E2kSecurityDescriptor *sd;
438         E2k_SECURITY_DESCRIPTOR_RELATIVE sdbuf;
439         guint16 off, header_len;
440
441         g_return_val_if_fail (xml_form != NULL, NULL);
442         g_return_val_if_fail (binary_form != NULL, NULL);
443
444         if (binary_form->len < 2)
445                 return NULL;
446
447         memcpy (&header_len, binary_form->data, 2);
448         header_len = GUINT16_FROM_LE (header_len);
449         if (header_len + sizeof (sdbuf) > binary_form->len)
450                 return NULL;
451
452         memcpy (&sdbuf, binary_form->data + header_len, sizeof (sdbuf));
453         if (sdbuf.Revision != E2K_SECURITY_DESCRIPTOR_REVISION)
454                 return NULL;
455         if ((sdbuf.Control & (E2K_SE_DACL_PRESENT | E2K_SE_SACL_PRESENT)) !=
456             E2K_SE_DACL_PRESENT)
457                 return NULL;
458
459         sd = g_object_new (E2K_TYPE_SECURITY_DESCRIPTOR, NULL);
460         sd->priv->header = g_byte_array_new ();
461         g_byte_array_append (sd->priv->header, binary_form->data, header_len);
462         sd->priv->control_flags = sdbuf.Control;
463
464         /* Create a SID for "Default" then extract remaining SIDs from
465          * the XML form since they have display names associated with
466          * them.
467          */
468         sd->priv->default_sid =
469                 e2k_sid_new_from_string_sid (E2K_SID_TYPE_WELL_KNOWN_GROUP,
470                                              E2K_SID_WKS_EVERYONE, NULL);
471         g_hash_table_insert (sd->priv->sids,
472                              (char *)e2k_sid_get_binary_sid (sd->priv->default_sid),
473                              sd->priv->default_sid);
474         extract_sids (sd, xml_form);
475
476         off = GUINT32_FROM_LE (sdbuf.Owner) + sd->priv->header->len;
477         if (!parse_sid (sd, binary_form, &off, &sd->priv->owner))
478                 goto lose;
479         off = GUINT32_FROM_LE (sdbuf.Group) + sd->priv->header->len;
480         if (!parse_sid (sd, binary_form, &off, &sd->priv->group))
481                 goto lose;
482
483         off = GUINT32_FROM_LE (sdbuf.Dacl) + sd->priv->header->len;
484         if (!parse_acl (sd, binary_form, &off))
485                 goto lose;
486
487         return sd;
488
489  lose:
490         g_object_unref (sd);
491         return NULL;
492 }
493
494 /**
495  * e2k_security_descriptor_to_binary:
496  * @sd: an #E2kSecurityDescriptor
497  *
498  * Converts @sd back to binary (#E2K_PR_EXCHANGE_SD_BINARY) form
499  * so it can be PROPPATCHed back to the server.
500  *
501  * Return value: the binary form of @sd.
502  **/
503 GByteArray *
504 e2k_security_descriptor_to_binary (E2kSecurityDescriptor *sd)
505 {
506         GByteArray *binsd;
507         E2k_SECURITY_DESCRIPTOR_RELATIVE sdbuf;
508         E2k_ACL aclbuf;
509         E2k_ACE *aces;
510         int off, ace, last_ace = -1, acl_size, ace_count;
511         const guint8 *bsid;
512
513         g_return_val_if_fail (E2K_IS_SECURITY_DESCRIPTOR (sd), NULL);
514
515         aces = (E2k_ACE *)sd->priv->aces->data;
516
517         /* Compute the length of the ACL first */
518         acl_size = sizeof (E2k_ACL);
519         for (ace = ace_count = 0; ace < sd->priv->aces->len; ace++) {
520                 if (aces[ace].Mask) {
521                         ace_count++;
522                         acl_size += GUINT16_FROM_LE (aces[ace].Header.AceSize);
523                 }
524         }
525
526         binsd = g_byte_array_new ();
527
528         /* Exchange-specific header */
529         g_byte_array_append (binsd, sd->priv->header->data,
530                              sd->priv->header->len);
531
532         /* SECURITY_DESCRIPTOR header */
533         memset (&sdbuf, 0, sizeof (sdbuf));
534         sdbuf.Revision = E2K_SECURITY_DESCRIPTOR_REVISION;
535         sdbuf.Control = sd->priv->control_flags;
536         off = sizeof (sdbuf);
537         sdbuf.Dacl = GUINT32_TO_LE (off);
538         off += acl_size;
539         sdbuf.Owner = GUINT32_TO_LE (off);
540         bsid = e2k_sid_get_binary_sid (sd->priv->owner);
541         off += E2K_SID_BINARY_SID_LEN (bsid);
542         sdbuf.Group = GUINT32_TO_LE (off);
543         g_byte_array_append (binsd, (gpointer)&sdbuf, sizeof (sdbuf));
544
545         /* ACL header */
546         aclbuf.AclRevision = E2K_ACL_REVISION;
547         aclbuf.Sbz1        = 0;
548         aclbuf.AclSize     = GUINT16_TO_LE (acl_size);
549         aclbuf.AceCount    = GUINT16_TO_LE (ace_count);
550         aclbuf.Sbz2        = 0;
551         g_byte_array_append (binsd, (gpointer)&aclbuf, sizeof (aclbuf));
552
553         /* ACEs */
554         for (ace = 0; ace < sd->priv->aces->len; ace++) {
555                 if (!aces[ace].Mask)
556                         continue;
557
558                 if (last_ace != -1) {
559                         if (ace_compar (&aces[last_ace], &aces[ace], sd) != -1) {
560                                 g_warning ("ACE order mismatch at %d\n", ace);
561                                 g_byte_array_free (binsd, TRUE);
562                                 return NULL;
563                         }
564                 }
565
566                 g_byte_array_append (binsd, (gpointer)&aces[ace],
567                                      sizeof (aces[ace].Header) +
568                                      sizeof (aces[ace].Mask));
569                 bsid = e2k_sid_get_binary_sid (aces[ace].Sid);
570                 g_byte_array_append (binsd, bsid,
571                                      E2K_SID_BINARY_SID_LEN (bsid));
572                 last_ace = ace;
573         }
574
575         /* Owner and Group */
576         bsid = e2k_sid_get_binary_sid (sd->priv->owner);
577         g_byte_array_append (binsd, bsid, E2K_SID_BINARY_SID_LEN (bsid));
578         bsid = e2k_sid_get_binary_sid (sd->priv->group);
579         g_byte_array_append (binsd, bsid, E2K_SID_BINARY_SID_LEN (bsid));
580
581         return binsd;
582 }
583
584 /**
585  * e2k_security_descriptor_get_default:
586  * @sd: a security descriptor
587  *
588  * Returns an #E2kSid corresponding to the default permissions
589  * associated with @sd. You can pass this to
590  * e2k_security_descriptor_get_permissions() and
591  * e2k_security_descriptor_set_permissions().
592  *
593  * Return value: the "Default" SID
594  **/
595 E2kSid *
596 e2k_security_descriptor_get_default (E2kSecurityDescriptor *sd)
597 {
598         return sd->priv->default_sid;
599 }
600
601 /**
602  * e2k_security_descriptor_get_sids:
603  * @sd: a security descriptor
604  *
605  * Returns a #GList containing the SIDs of each user or group
606  * represented in @sd. You can pass these SIDs to
607  * e2k_security_descriptor_get_permissions(),
608  * e2k_security_descriptor_set_permissions(), and
609  * e2k_security_descriptor_remove_sid().
610  *
611  * Return value: a list of SIDs. The caller must free the list
612  * with g_list_free(), but should not free the contents.
613  **/
614 GList *
615 e2k_security_descriptor_get_sids (E2kSecurityDescriptor *sd)
616 {
617         GList *sids = NULL;
618         GHashTable *added_sids;
619         E2k_ACE *aces;
620         int ace;
621
622         g_return_val_if_fail (E2K_IS_SECURITY_DESCRIPTOR (sd), NULL);
623
624         added_sids = g_hash_table_new (NULL, NULL);
625         aces = (E2k_ACE *)sd->priv->aces->data;
626         for (ace = 0; ace < sd->priv->aces->len; ace++) {
627                 if (!g_hash_table_lookup (added_sids, aces[ace].Sid)) {
628                         g_hash_table_insert (added_sids, aces[ace].Sid,
629                                              aces[ace].Sid);
630                         sids = g_list_prepend (sids, aces[ace].Sid);
631                 }
632         }
633         g_hash_table_destroy (added_sids);
634
635         return sids;
636 }
637
638 /**
639  * e2k_security_descriptor_remove_sid:
640  * @sd: a security descriptor
641  * @sid: a SID
642  *
643  * Removes @sid from @sd. If @sid is a user, this means s/he will now
644  * have only the default permissions on @sd (unless s/he is a member
645  * of a group that is also present in @sd.)
646  **/
647 void
648 e2k_security_descriptor_remove_sid (E2kSecurityDescriptor *sd,
649                                     E2kSid *sid)
650 {
651         E2k_ACE *aces;
652         int ace;
653
654         g_return_if_fail (E2K_IS_SECURITY_DESCRIPTOR (sd));
655         g_return_if_fail (E2K_IS_SID (sid));
656
657         /* Canonicalize the SID */
658         sid = g_hash_table_lookup (sd->priv->sids,
659                                    e2k_sid_get_binary_sid (sid));
660         if (!sid)
661                 return;
662
663         /* We can't actually remove all trace of the user, because if
664          * he is removed and then re-added without saving in between,
665          * then we need to keep the original AceFlags. So we just
666          * clear out all of the masks, which (assuming the user is
667          * not re-added) will result in him not being written out
668          * when sd is saved.
669          */
670
671         aces = (E2k_ACE *)sd->priv->aces->data;
672         for (ace = 0; ace < sd->priv->aces->len; ace++) {
673                 if (aces[ace].Sid == sid)
674                         aces[ace].Mask = 0;
675         }
676 }
677
678 /**
679  * e2k_security_descriptor_get_permissions:
680  * @sd: a security descriptor
681  * @sid: a SID
682  *
683  * Computes the MAPI permissions associated with @sid. (Only the
684  * permissions *directly* associated with @sid, not any acquired via
685  * group memberships or the Default SID.)
686  *
687  * Return value: the MAPI permissions
688  **/
689 guint32
690 e2k_security_descriptor_get_permissions (E2kSecurityDescriptor *sd,
691                                          E2kSid *sid)
692 {
693         E2k_ACE *aces;
694         guint32 mapi_perms, checkperm;
695         int ace, map;
696
697         g_return_val_if_fail (E2K_IS_SECURITY_DESCRIPTOR (sd), 0);
698         g_return_val_if_fail (E2K_IS_SID (sid), 0);
699
700         /* Canonicalize the SID */
701         sid = g_hash_table_lookup (sd->priv->sids,
702                                    e2k_sid_get_binary_sid (sid));
703         if (!sid)
704                 return 0;
705
706         mapi_perms = 0;
707         aces = (E2k_ACE *)sd->priv->aces->data;
708         for (ace = 0; ace < sd->priv->aces->len; ace++) {
709                 if (aces[ace].Sid != sid)
710                         continue;
711                 if (aces[ace].Header.AceType == E2K_ACCESS_DENIED_ACE_TYPE)
712                         continue;
713
714                 for (map = 0; map < permissions_map_size; map++) {
715                         if (aces[ace].Header.AceFlags & E2K_OBJECT_INHERIT_ACE)
716                                 checkperm = permissions_map[map].object_allowed;
717                         else
718                                 checkperm = permissions_map[map].container_allowed;
719                         if (!checkperm)
720                                 continue;
721
722                         if ((aces[ace].Mask & checkperm) == checkperm)
723                                 mapi_perms |= permissions_map[map].mapi_permission;
724                 }
725         }
726
727         return mapi_perms;
728 }
729
730 /* Put @ace into @sd. If no ACE corresponding to @ace currently exists,
731  * it will be added in the right place. If it does already exist, its
732  * flags (in particular INHERITED_ACE) will be preserved and only the
733  * mask will be changed.
734  */
735 static void
736 set_ace (E2kSecurityDescriptor *sd, E2k_ACE *ace)
737 {
738         E2k_ACE *aces = (E2k_ACE *)sd->priv->aces->data;
739         int low, mid = 0, high, cmp = -1;
740
741         low = 0;
742         high = sd->priv->aces->len - 1;
743         while (low <= high) {
744                 mid = (low + high) / 2;
745                 cmp = ace_compar (ace, &aces[mid], sd);
746                 if (cmp == 0) {
747                         if (ace->Mask)
748                                 aces[mid].Mask = ace->Mask;
749                         else
750                                 g_array_remove_index (sd->priv->aces, mid);
751                         return;
752                 } else if (cmp < 0)
753                         high = mid - 1;
754                 else
755                         low = mid + 1;
756         }
757
758         if (ace->Mask)
759                 g_array_insert_vals (sd->priv->aces, cmp < 0 ? mid : mid + 1, ace, 1);
760 }
761
762 /**
763  * e2k_security_descriptor_set_permissions:
764  * @sd: a security descriptor
765  * @sid: a SID
766  * @perms: the MAPI permissions
767  *
768  * Updates or sets @sid's permissions on @sd.
769  **/
770 void
771 e2k_security_descriptor_set_permissions (E2kSecurityDescriptor *sd,
772                                          E2kSid *sid, guint32 perms)
773 {
774         E2k_ACE ace;
775         guint32 object_allowed, object_denied;
776         guint32 container_allowed, container_denied;
777         const guint8 *bsid;
778         E2kSid *sid2;
779         int map;
780
781         g_return_if_fail (E2K_IS_SECURITY_DESCRIPTOR (sd));
782         g_return_if_fail (E2K_IS_SID (sid));
783
784         bsid = e2k_sid_get_binary_sid (sid);
785         sid2 = g_hash_table_lookup (sd->priv->sids, bsid);
786         if (!sid2) {
787                 int size = g_hash_table_size (sd->priv->sid_order);
788
789                 g_hash_table_insert (sd->priv->sids, (char *)bsid, sid);
790                 g_object_ref (sid);
791
792                 g_hash_table_insert (sd->priv->sid_order, sid,
793                                      GUINT_TO_POINTER (size + 1));
794         } else
795                 sid = sid2;
796
797         object_allowed    = 0;
798         object_denied     = object_permissions_all;
799         container_allowed = 0;
800         container_denied  = container_permissions_all;
801
802         for (map = 0; map < permissions_map_size; map++) {
803                 if (!(permissions_map[map].mapi_permission & perms))
804                         continue;
805
806                 object_allowed    |=  permissions_map[map].object_allowed;
807                 object_denied     &= ~permissions_map[map].object_not_denied;
808                 container_allowed |=  permissions_map[map].container_allowed;
809                 container_denied  &= ~permissions_map[map].container_not_denied;
810         }
811
812         ace.Sid = sid;
813         ace.Header.AceSize = GUINT16_TO_LE (sizeof (ace.Header) +
814                                             sizeof (ace.Mask) +
815                                             E2K_SID_BINARY_SID_LEN (bsid));
816
817         ace.Header.AceType  = E2K_ACCESS_ALLOWED_ACE_TYPE;
818         ace.Header.AceFlags = E2K_OBJECT_INHERIT_ACE | E2K_INHERIT_ONLY_ACE;
819         ace.Mask = object_allowed;
820         set_ace (sd, &ace);
821         if (sid != sd->priv->default_sid) {
822                 ace.Header.AceType  = E2K_ACCESS_DENIED_ACE_TYPE;
823                 ace.Header.AceFlags = E2K_OBJECT_INHERIT_ACE | E2K_INHERIT_ONLY_ACE;
824                 ace.Mask = object_denied;
825                 set_ace (sd, &ace);
826         }
827         
828         ace.Header.AceType  = E2K_ACCESS_ALLOWED_ACE_TYPE;
829         ace.Header.AceFlags = E2K_CONTAINER_INHERIT_ACE;
830         ace.Mask = container_allowed;
831         set_ace (sd, &ace);
832         if (sid != sd->priv->default_sid) {
833                 ace.Header.AceType  = E2K_ACCESS_DENIED_ACE_TYPE;
834                 ace.Header.AceFlags = E2K_CONTAINER_INHERIT_ACE;
835                 ace.Mask = container_denied;
836                 set_ace (sd, &ace);
837         }
838 }
839
840
841 static struct {
842         const char *name;
843         guint32 perms;
844 } roles[E2K_PERMISSIONS_ROLE_NUM_ROLES] = {
845         /* i18n: These are Outlook's words for the default roles in
846            the folder permissions dialog. */
847         { N_("Owner"),             (E2K_PERMISSION_FOLDER_VISIBLE |
848                                     E2K_PERMISSION_READ_ANY |
849                                     E2K_PERMISSION_CREATE |
850                                     E2K_PERMISSION_DELETE_OWNED |
851                                     E2K_PERMISSION_EDIT_OWNED |
852                                     E2K_PERMISSION_DELETE_ANY |
853                                     E2K_PERMISSION_EDIT_ANY |
854                                     E2K_PERMISSION_CREATE_SUBFOLDER |
855                                     E2K_PERMISSION_CONTACT |
856                                     E2K_PERMISSION_OWNER) },
857         { N_("Publishing Editor"), (E2K_PERMISSION_FOLDER_VISIBLE |
858                                     E2K_PERMISSION_READ_ANY |
859                                     E2K_PERMISSION_CREATE |
860                                     E2K_PERMISSION_DELETE_OWNED |
861                                     E2K_PERMISSION_EDIT_OWNED |
862                                     E2K_PERMISSION_DELETE_ANY |
863                                     E2K_PERMISSION_EDIT_ANY |
864                                     E2K_PERMISSION_CREATE_SUBFOLDER) },
865         { N_("Editor"),            (E2K_PERMISSION_FOLDER_VISIBLE |
866                                     E2K_PERMISSION_READ_ANY |
867                                     E2K_PERMISSION_CREATE |
868                                     E2K_PERMISSION_DELETE_OWNED |
869                                     E2K_PERMISSION_EDIT_OWNED |
870                                     E2K_PERMISSION_DELETE_ANY |
871                                     E2K_PERMISSION_EDIT_ANY) },
872         { N_("Publishing Author"), (E2K_PERMISSION_FOLDER_VISIBLE |
873                                     E2K_PERMISSION_READ_ANY |
874                                     E2K_PERMISSION_CREATE |
875                                     E2K_PERMISSION_DELETE_OWNED |
876                                     E2K_PERMISSION_EDIT_OWNED |
877                                     E2K_PERMISSION_CREATE_SUBFOLDER) },
878         { N_("Author"),            (E2K_PERMISSION_FOLDER_VISIBLE |
879                                     E2K_PERMISSION_READ_ANY |
880                                     E2K_PERMISSION_CREATE |
881                                     E2K_PERMISSION_DELETE_OWNED |
882                                     E2K_PERMISSION_EDIT_OWNED) },
883         { N_("Non-editing Author"),(E2K_PERMISSION_FOLDER_VISIBLE |
884                                     E2K_PERMISSION_READ_ANY |
885                                     E2K_PERMISSION_CREATE |
886                                     E2K_PERMISSION_DELETE_OWNED) },
887         { N_("Reviewer"),          (E2K_PERMISSION_FOLDER_VISIBLE |
888                                     E2K_PERMISSION_READ_ANY) },
889         { N_("Contributor"),       (E2K_PERMISSION_FOLDER_VISIBLE |
890                                     E2K_PERMISSION_CREATE) },
891         { N_("None"),              (E2K_PERMISSION_FOLDER_VISIBLE) }
892 };
893
894 /**
895  * e2k_permissions_role_get_name:
896  * @role: a permissions role
897  *
898  * Returns the localized name corresponding to @role
899  *
900  * Return value: the name
901  **/
902 const char *
903 e2k_permissions_role_get_name (E2kPermissionsRole role)
904 {
905         if (role == E2K_PERMISSIONS_ROLE_CUSTOM)
906                 return _("Custom");
907
908         g_return_val_if_fail (role > E2K_PERMISSIONS_ROLE_CUSTOM &&
909                               role < E2K_PERMISSIONS_ROLE_NUM_ROLES, NULL);
910         return _(roles[role].name);
911 }
912
913 /**
914  * e2k_permissions_role_get_perms
915  * @role: a permissions role
916  *
917  * Returns the MAPI permissions associated with @role. @role may not
918  * be %E2K_PERMISSIONS_ROLE_CUSTOM.
919  *
920  * Return value: the MAPI permissions
921  **/
922 guint32
923 e2k_permissions_role_get_perms (E2kPermissionsRole role)
924 {
925         g_return_val_if_fail (role >= E2K_PERMISSIONS_ROLE_CUSTOM &&
926                               role < E2K_PERMISSIONS_ROLE_NUM_ROLES, 0);
927         return roles[role].perms;
928 }
929
930 /**
931  * e2k_permissions_role_find:
932  * @perms: MAPI permissions
933  *
934  * Finds the #E2kPermissionsRole value associated with @perms. If
935  * @perms don't describe any standard role, the return value will be
936  * %E2K_PERMISSIONS_ROLE_CUSTOM
937  *
938  * Return value: the role
939  **/
940 E2kPermissionsRole
941 e2k_permissions_role_find (guint perms)
942 {
943         int role;
944
945         /* "Folder contact" isn't actually a permission, and is ignored
946          * for purposes of roles.
947          */
948         perms &= ~E2K_PERMISSION_CONTACT;
949
950         /* The standard "None" permission includes "Folder visible",
951          * but 0 counts as "None" too.
952          */
953         if (perms == 0)
954                 return E2K_PERMISSIONS_ROLE_NONE;
955
956         for (role = 0; role < E2K_PERMISSIONS_ROLE_NUM_ROLES; role++) {
957                 if ((roles[role].perms & ~E2K_PERMISSION_CONTACT) == perms)
958                         return role;
959         }
960
961         return E2K_PERMISSIONS_ROLE_CUSTOM;
962 }