/** * e2k_security_descriptor_remove_sid: * @sd: a security descriptor * @sid: a SID * * Removes @sid from @sd. If @sid is a user, this means s/he will now * have only the default permissions on @sd (unless s/he is a member * of a group that is also present in @sd.) **/ void e2k_security_descriptor_remove_sid (E2kSecurityDescriptor *sd, E2kSid *sid) { E2k_ACE *aces; int ace; g_return_if_fail (E2K_IS_SECURITY_DESCRIPTOR (sd)); g_return_if_fail (E2K_IS_SID (sid)); /* Canonicalize the SID */ sid = g_hash_table_lookup (sd->priv->sids, e2k_sid_get_binary_sid (sid)); if (!sid) return; /* We can't actually remove all trace of the user, because if * he is removed and then re-added without saving in between, * then we need to keep the original AceFlags. So we just * clear out all of the masks, which (assuming the user is * not re-added) will result in him not being written out * when sd is saved. */ aces = (E2k_ACE *)sd->priv->aces->data; for (ace = 0; ace < sd->priv->aces->len; ace++) { if (aces[ace].Sid == sid) aces[ace].Mask = 0; } }
/** * e2k_security_descriptor_new: * @xml_form: the XML form of the folder's security descriptor * (The "http://schemas.microsoft.com/exchange/security/descriptor" * property, aka %E2K_PR_EXCHANGE_SD_XML) * @binary_form: the binary form of the folder's security descriptor * (The "http://schemas.microsoft.com/exchange/ntsecuritydescriptor" * property, aka %E2K_PR_EXCHANGE_SD_BINARY) * * Constructs an #E2kSecurityDescriptor from the data in @xml_form and * @binary_form. * * Return value: the security descriptor, or %NULL if the data could * not be parsed. **/ E2kSecurityDescriptor * e2k_security_descriptor_new (xmlNodePtr xml_form, GByteArray *binary_form) { E2kSecurityDescriptor *sd; E2k_SECURITY_DESCRIPTOR_RELATIVE sdbuf; guint16 off, header_len; g_return_val_if_fail (xml_form != NULL, NULL); g_return_val_if_fail (binary_form != NULL, NULL); if (binary_form->len < 2) return NULL; memcpy (&header_len, binary_form->data, 2); header_len = GUINT16_FROM_LE (header_len); if (header_len + sizeof (sdbuf) > binary_form->len) return NULL; memcpy (&sdbuf, binary_form->data + header_len, sizeof (sdbuf)); if (sdbuf.Revision != E2K_SECURITY_DESCRIPTOR_REVISION) return NULL; if ((sdbuf.Control & (E2K_SE_DACL_PRESENT | E2K_SE_SACL_PRESENT)) != E2K_SE_DACL_PRESENT) return NULL; sd = g_object_new (E2K_TYPE_SECURITY_DESCRIPTOR, NULL); sd->priv->header = g_byte_array_new (); g_byte_array_append (sd->priv->header, binary_form->data, header_len); sd->priv->control_flags = sdbuf.Control; /* Create a SID for "Default" then extract remaining SIDs from * the XML form since they have display names associated with * them. */ sd->priv->default_sid = e2k_sid_new_from_string_sid (E2K_SID_TYPE_WELL_KNOWN_GROUP, E2K_SID_WKS_EVERYONE, NULL); g_hash_table_insert (sd->priv->sids, (char *)e2k_sid_get_binary_sid (sd->priv->default_sid), sd->priv->default_sid); extract_sids (sd, xml_form); off = GUINT32_FROM_LE (sdbuf.Owner) + sd->priv->header->len; if (!parse_sid (sd, binary_form, &off, &sd->priv->owner)) goto lose; off = GUINT32_FROM_LE (sdbuf.Group) + sd->priv->header->len; if (!parse_sid (sd, binary_form, &off, &sd->priv->group)) goto lose; off = GUINT32_FROM_LE (sdbuf.Dacl) + sd->priv->header->len; if (!parse_acl (sd, binary_form, &off)) goto lose; return sd; lose: g_object_unref (sd); return NULL; }
static void extract_sids (E2kSecurityDescriptor *sd, xmlNodePtr node) { xmlNodePtr string_sid_node, type_node, display_name_node; char *string_sid, *content, *display_name; const guint8 *bsid; E2kSid *sid; E2kSidType type; for (; node; node = node->next) { if (strcmp (node->name, "sid") != 0) { if (node->xmlChildrenNode) extract_sids (sd, node->xmlChildrenNode); continue; } string_sid_node = find_child (node, "string_sid"); type_node = find_child (node, "type"); display_name_node = find_child (node, "display_name"); if (!string_sid_node || !type_node) continue; string_sid = xmlNodeGetContent (string_sid_node); content = xmlNodeGetContent (type_node); if (!content || !strcmp (content, "user")) type = E2K_SID_TYPE_USER; else if (!strcmp (content, "group")) type = E2K_SID_TYPE_GROUP; else if (!strcmp (content, "well_known_group")) type = E2K_SID_TYPE_WELL_KNOWN_GROUP; else if (!strcmp (content, "alias")) type = E2K_SID_TYPE_ALIAS; else type = E2K_SID_TYPE_INVALID; xmlFree (content); if (display_name_node) display_name = xmlNodeGetContent (display_name_node); else display_name = NULL; sid = e2k_sid_new_from_string_sid (type, string_sid, display_name); xmlFree (string_sid); if (display_name) xmlFree (display_name); bsid = e2k_sid_get_binary_sid (sid); if (g_hash_table_lookup (sd->priv->sids, bsid)) { g_object_unref (sid); continue; } g_hash_table_insert (sd->priv->sids, (char *)bsid, sid); } }
/* Add or remove a delegate. Everyone must be in one of three states: * 1. only in users (because they started and ended there) * 2. in users and added_users (because they weren't in * users to begin with, but got added) * 3. only in removed_users (because they were in users to * begin with and got removed). * If you're added and then removed, or removed and then added, you have * to end up in state 1. That's what this is for. */ static void add_remove_user (ExchangeDelegatesUser *user, GPtrArray *to_array, GPtrArray *from_array) { ExchangeDelegatesUser *match; gint i; for (i = 0; i < from_array->len; i++) { match = from_array->pdata[i]; if (e2k_sid_binary_sid_equal (e2k_sid_get_binary_sid (match->sid), e2k_sid_get_binary_sid (user->sid))) { g_ptr_array_remove_index_fast (from_array, i); g_object_unref (match); return; } } g_ptr_array_add (to_array, user); g_object_ref (user); }
/** * e2k_security_descriptor_get_permissions: * @sd: a security descriptor * @sid: a SID * * Computes the MAPI permissions associated with @sid. (Only the * permissions *directly* associated with @sid, not any acquired via * group memberships or the Default SID.) * * Return value: the MAPI permissions **/ guint32 e2k_security_descriptor_get_permissions (E2kSecurityDescriptor *sd, E2kSid *sid) { E2k_ACE *aces; guint32 mapi_perms, checkperm; int ace, map; g_return_val_if_fail (E2K_IS_SECURITY_DESCRIPTOR (sd), 0); g_return_val_if_fail (E2K_IS_SID (sid), 0); /* Canonicalize the SID */ sid = g_hash_table_lookup (sd->priv->sids, e2k_sid_get_binary_sid (sid)); if (!sid) return 0; mapi_perms = 0; aces = (E2k_ACE *)sd->priv->aces->data; for (ace = 0; ace < sd->priv->aces->len; ace++) { if (aces[ace].Sid != sid) continue; if (aces[ace].Header.AceType == E2K_ACCESS_DENIED_ACE_TYPE) continue; for (map = 0; map < permissions_map_size; map++) { if (aces[ace].Header.AceFlags & E2K_OBJECT_INHERIT_ACE) checkperm = permissions_map[map].object_allowed; else checkperm = permissions_map[map].container_allowed; if (!checkperm) continue; if ((aces[ace].Mask & checkperm) == checkperm) mapi_perms |= permissions_map[map].mapi_permission; } } return mapi_perms; }
static void add_button_clicked_cb (GtkWidget *widget, gpointer data) { ExchangeDelegates *delegates = data; E2kGlobalCatalog *gc; GtkWidget *dialog, *parent_window; const gchar *delegate_exchange_dn; gchar *email; ExchangeDelegatesUser *user, *match; gint response, u; GtkTreeIter iter; if (!get_folder_security (delegates)) return; gc = exchange_account_get_global_catalog (delegates->account); parent_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW); dialog = e2k_user_dialog_new (parent_window, _("Delegate To:"), _("Delegate To")); response = gtk_dialog_run (GTK_DIALOG (dialog)); if (response != GTK_RESPONSE_OK) { gtk_widget_destroy (dialog); return; } email = e2k_user_dialog_get_user (E2K_USER_DIALOG (dialog)); gtk_widget_destroy (dialog); if (email == NULL) return; user = exchange_delegates_user_new_from_gc (gc, email, delegates->creator_entryid); if (!user) { e_alert_run_dialog_for_args (GTK_WINDOW (parent_window), ERROR_DOMAIN ":delegate-error", email, NULL); g_free (email); return; } g_free (email); delegate_exchange_dn = e2k_entryid_to_dn (user->entryid); if (delegate_exchange_dn && !g_ascii_strcasecmp (delegate_exchange_dn, delegates->account->legacy_exchange_dn)) { g_object_unref (user); e_alert_run_dialog_for_args (GTK_WINDOW (parent_window), ERROR_DOMAIN ":delegate-own-error", NULL); return; } for (u = 0; u < delegates->users->len; u++) { match = delegates->users->pdata[u]; if (e2k_sid_binary_sid_equal (e2k_sid_get_binary_sid (user->sid), e2k_sid_get_binary_sid (match->sid))) { e_alert_run_dialog_for_args (GTK_WINDOW (parent_window), ERROR_DOMAIN ":delegate-existing", user->display_name, NULL); g_object_unref (user); exchange_delegates_user_edit (delegates->account, match, parent_window); return; } } if (!exchange_delegates_user_edit (delegates->account, user, parent_window)) { g_object_unref (user); return; } set_perms_for_user (user, delegates); g_signal_connect (user, "edited", G_CALLBACK (set_perms_for_user), delegates); add_remove_user (user, delegates->added_users, delegates->removed_users); g_ptr_array_add (delegates->users, user); /* Add the user to the table */ gtk_list_store_append (delegates->model, &iter); gtk_list_store_set (delegates->model, &iter, 0, user->display_name, -1); }
/** * e2k_security_descriptor_set_permissions: * @sd: a security descriptor * @sid: a SID * @perms: the MAPI permissions * * Updates or sets @sid's permissions on @sd. **/ void e2k_security_descriptor_set_permissions (E2kSecurityDescriptor *sd, E2kSid *sid, guint32 perms) { E2k_ACE ace; guint32 object_allowed, object_denied; guint32 container_allowed, container_denied; const guint8 *bsid; E2kSid *sid2; int map; g_return_if_fail (E2K_IS_SECURITY_DESCRIPTOR (sd)); g_return_if_fail (E2K_IS_SID (sid)); bsid = e2k_sid_get_binary_sid (sid); sid2 = g_hash_table_lookup (sd->priv->sids, bsid); if (!sid2) { int size = g_hash_table_size (sd->priv->sid_order); g_hash_table_insert (sd->priv->sids, (char *)bsid, sid); g_object_ref (sid); g_hash_table_insert (sd->priv->sid_order, sid, GUINT_TO_POINTER (size + 1)); } else sid = sid2; object_allowed = 0; object_denied = object_permissions_all; container_allowed = 0; container_denied = container_permissions_all; for (map = 0; map < permissions_map_size; map++) { if (!(permissions_map[map].mapi_permission & perms)) continue; object_allowed |= permissions_map[map].object_allowed; object_denied &= ~permissions_map[map].object_not_denied; container_allowed |= permissions_map[map].container_allowed; container_denied &= ~permissions_map[map].container_not_denied; } ace.Sid = sid; ace.Header.AceSize = GUINT16_TO_LE (sizeof (ace.Header) + sizeof (ace.Mask) + E2K_SID_BINARY_SID_LEN (bsid)); ace.Header.AceType = E2K_ACCESS_ALLOWED_ACE_TYPE; ace.Header.AceFlags = E2K_OBJECT_INHERIT_ACE | E2K_INHERIT_ONLY_ACE; ace.Mask = object_allowed; set_ace (sd, &ace); if (sid != sd->priv->default_sid) { ace.Header.AceType = E2K_ACCESS_DENIED_ACE_TYPE; ace.Header.AceFlags = E2K_OBJECT_INHERIT_ACE | E2K_INHERIT_ONLY_ACE; ace.Mask = object_denied; set_ace (sd, &ace); } ace.Header.AceType = E2K_ACCESS_ALLOWED_ACE_TYPE; ace.Header.AceFlags = E2K_CONTAINER_INHERIT_ACE; ace.Mask = container_allowed; set_ace (sd, &ace); if (sid != sd->priv->default_sid) { ace.Header.AceType = E2K_ACCESS_DENIED_ACE_TYPE; ace.Header.AceFlags = E2K_CONTAINER_INHERIT_ACE; ace.Mask = container_denied; set_ace (sd, &ace); } }
/** * e2k_security_descriptor_to_binary: * @sd: an #E2kSecurityDescriptor * * Converts @sd back to binary (#E2K_PR_EXCHANGE_SD_BINARY) form * so it can be PROPPATCHed back to the server. * * Return value: the binary form of @sd. **/ GByteArray * e2k_security_descriptor_to_binary (E2kSecurityDescriptor *sd) { GByteArray *binsd; E2k_SECURITY_DESCRIPTOR_RELATIVE sdbuf; E2k_ACL aclbuf; E2k_ACE *aces; int off, ace, last_ace = -1, acl_size, ace_count; const guint8 *bsid; g_return_val_if_fail (E2K_IS_SECURITY_DESCRIPTOR (sd), NULL); aces = (E2k_ACE *)sd->priv->aces->data; /* Compute the length of the ACL first */ acl_size = sizeof (E2k_ACL); for (ace = ace_count = 0; ace < sd->priv->aces->len; ace++) { if (aces[ace].Mask) { ace_count++; acl_size += GUINT16_FROM_LE (aces[ace].Header.AceSize); } } binsd = g_byte_array_new (); /* Exchange-specific header */ g_byte_array_append (binsd, sd->priv->header->data, sd->priv->header->len); /* SECURITY_DESCRIPTOR header */ memset (&sdbuf, 0, sizeof (sdbuf)); sdbuf.Revision = E2K_SECURITY_DESCRIPTOR_REVISION; sdbuf.Control = sd->priv->control_flags; off = sizeof (sdbuf); sdbuf.Dacl = GUINT32_TO_LE (off); off += acl_size; sdbuf.Owner = GUINT32_TO_LE (off); bsid = e2k_sid_get_binary_sid (sd->priv->owner); off += E2K_SID_BINARY_SID_LEN (bsid); sdbuf.Group = GUINT32_TO_LE (off); g_byte_array_append (binsd, (gpointer)&sdbuf, sizeof (sdbuf)); /* ACL header */ aclbuf.AclRevision = E2K_ACL_REVISION; aclbuf.Sbz1 = 0; aclbuf.AclSize = GUINT16_TO_LE (acl_size); aclbuf.AceCount = GUINT16_TO_LE (ace_count); aclbuf.Sbz2 = 0; g_byte_array_append (binsd, (gpointer)&aclbuf, sizeof (aclbuf)); /* ACEs */ for (ace = 0; ace < sd->priv->aces->len; ace++) { if (!aces[ace].Mask) continue; if (last_ace != -1) { if (ace_compar (&aces[last_ace], &aces[ace], sd) != -1) { g_warning ("ACE order mismatch at %d\n", ace); g_byte_array_free (binsd, TRUE); return NULL; } } g_byte_array_append (binsd, (gpointer)&aces[ace], sizeof (aces[ace].Header) + sizeof (aces[ace].Mask)); bsid = e2k_sid_get_binary_sid (aces[ace].Sid); g_byte_array_append (binsd, bsid, E2K_SID_BINARY_SID_LEN (bsid)); last_ace = ace; } /* Owner and Group */ bsid = e2k_sid_get_binary_sid (sd->priv->owner); g_byte_array_append (binsd, bsid, E2K_SID_BINARY_SID_LEN (bsid)); bsid = e2k_sid_get_binary_sid (sd->priv->group); g_byte_array_append (binsd, bsid, E2K_SID_BINARY_SID_LEN (bsid)); return binsd; }