2 OpenChange Server implementation.
4 EMSABP: Address Book Provider implementation
6 Copyright (C) Julien Kerihuel 2006-2009.
7 Copyright (C) Pauline Khun 2006.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 \brief Address Book Provider implementation
29 #include "mapiproxy/dcesrv_mapiproxy.h"
30 #include "mapiproxy/libmapiproxy.h"
31 #include "dcesrv_exchange_nsp.h"
33 #include <ldb_errors.h>
34 #include <util/debug.h>
37 \details Initialize the EMSABP context and open connections to
40 \param lp_ctx pointer to the loadparm context
41 \param tdb_ctx pointer to the EMSABP TDB context
43 \return Allocated emsabp_context on success, otherwise NULL
45 _PUBLIC_ struct emsabp_context *emsabp_init(struct loadparm_context *lp_ctx,
49 struct emsabp_context *emsabp_ctx;
50 struct tevent_context *ev;
51 char *configuration = NULL;
56 if (!lp_ctx) return NULL;
58 mem_ctx = talloc_named(NULL, 0, "emsabp_init");
60 emsabp_ctx = talloc_zero(mem_ctx, struct emsabp_context);
66 emsabp_ctx->mem_ctx = mem_ctx;
68 ev = tevent_context_init(mem_ctx);
74 /* Save a pointer to the loadparm context */
75 emsabp_ctx->lp_ctx = lp_ctx;
77 /* Return an opaque context pointer on the configuration database */
78 configuration = private_path(mem_ctx, lp_ctx, "configuration.ldb");
79 emsabp_ctx->conf_ctx = ldb_init(mem_ctx, ev);
80 if (!emsabp_ctx->conf_ctx) {
81 talloc_free(configuration);
86 ret = ldb_connect(emsabp_ctx->conf_ctx, configuration, LDB_FLG_RDONLY, NULL);
87 talloc_free(configuration);
88 if (ret != LDB_SUCCESS) {
89 DEBUG(0, ("[%s:%d]: Connection to \"configuration.ldb\" failed\n", __FUNCTION__, __LINE__));
94 /* Return an opaque context pointer to the users database */
95 users = private_path(mem_ctx, lp_ctx, "users.ldb");
96 emsabp_ctx->users_ctx = ldb_init(mem_ctx, ev);
97 if (!emsabp_ctx->users_ctx) {
103 ret = ldb_connect(emsabp_ctx->users_ctx, users, LDB_FLG_RDONLY, NULL);
105 if (ret != LDB_SUCCESS) {
106 DEBUG(0, ("[%s:%d]: Connection to \"users.ldb\" failed\n", __FUNCTION__, __LINE__));
107 talloc_free(mem_ctx);
111 /* Reference the global TDB context to the current emsabp context */
112 emsabp_ctx->tdb_ctx = tdb_ctx;
114 /* Initialize a temporary (on-memory) TDB database to store
115 * temporary MId used within EMSABP */
116 emsabp_ctx->ttdb_ctx = emsabp_tdb_init_tmp(emsabp_ctx->mem_ctx);
117 if (!emsabp_ctx->ttdb_ctx) {
118 smb_panic("unable to create on-memory TDB database");
125 _PUBLIC_ bool emsabp_destructor(void *data)
127 struct emsabp_context *emsabp_ctx = (struct emsabp_context *)data;
130 if (emsabp_ctx->ttdb_ctx) {
131 tdb_close(emsabp_ctx->ttdb_ctx);
134 talloc_free(emsabp_ctx->mem_ctx);
143 \details Check if the authenticated user belongs to the Exchange
146 \param dce_call pointer to the session context
147 \param emsabp_ctx pointer to the EMSABP context
149 \return true on success, otherwise false
151 _PUBLIC_ bool emsabp_verify_user(struct dcesrv_call_state *dce_call,
152 struct emsabp_context *emsabp_ctx)
155 const char *username = NULL;
156 int msExchUserAccountControl;
157 enum ldb_scope scope = LDB_SCOPE_SUBTREE;
158 struct ldb_result *res = NULL;
160 const char * const recipient_attrs[] = { "msExchUserAccountControl", NULL };
162 username = dce_call->context->conn->auth_state.session_info->server_info->account_name;
164 ldb_filter = talloc_asprintf(emsabp_ctx->mem_ctx, "CN=%s", username);
165 ret = ldb_search(emsabp_ctx->users_ctx, emsabp_ctx->mem_ctx, &res,
166 ldb_get_default_basedn(emsabp_ctx->users_ctx),
167 scope, recipient_attrs, ldb_filter);
168 talloc_free(ldb_filter);
170 /* If the search failed */
171 if (ret != LDB_SUCCESS || !res->count) {
175 /* If msExchUserAccountControl attribute is not found */
176 if (!res->msgs[0]->num_elements) {
180 /* If the attribute exists check its value */
181 msExchUserAccountControl = ldb_msg_find_attr_as_int(res->msgs[0], "msExchUserAccountControl", 2);
182 if (msExchUserAccountControl == 2) {
191 \details Check if the provided codepage is correct
193 \param emsabp_ctx pointer to the EMSABP context
194 \param CodePage the codepage identifier
196 \note The prototype is currently incorrect, but we are looking for
197 a better way to check codepage, maybe within AD. At the moment this
198 function is just a wrapper over libmapi valid_codepage function.
200 \return true on success, otherwise false
202 _PUBLIC_ bool emsabp_verify_codepage(struct emsabp_context *emsabp_ctx,
205 return valid_codepage(CodePage);
210 \details Retrieve the NSPI server GUID from the server object in
211 the configuration LDB database
213 \param emsabp_ctx pointer to the EMSABP context
215 \return An allocated GUID structure on success, otherwise NULL
217 _PUBLIC_ struct GUID *emsabp_get_server_GUID(struct emsabp_context *emsabp_ctx)
220 struct loadparm_context *lp_ctx;
221 struct GUID *guid = (struct GUID *) NULL;
222 const char *netbiosname = NULL;
223 const char *guid_str = NULL;
224 enum ldb_scope scope = LDB_SCOPE_SUBTREE;
225 struct ldb_result *res = NULL;
228 struct ldb_dn *ldb_dn = NULL;
229 const char * const recipient_attrs[] = { "*", NULL };
230 const char *firstorgdn = NULL;
232 lp_ctx = emsabp_ctx->lp_ctx;
234 netbiosname = lp_netbios_name(lp_ctx);
235 if (!netbiosname) return NULL;
237 /* Step 1. Find the Exchange Organization */
238 ldb_filter = talloc_strdup(emsabp_ctx->mem_ctx, "(objectClass=msExchOrganizationContainer)");
239 ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res,
240 ldb_get_default_basedn(emsabp_ctx->conf_ctx),
241 scope, recipient_attrs, ldb_filter);
242 talloc_free(ldb_filter);
244 if (ret != LDB_SUCCESS || !res->count) {
248 firstorgdn = ldb_msg_find_attr_as_string(res->msgs[0], "distinguishedName", NULL);
253 /* Step 2. Find the OpenChange Server object */
254 dn = talloc_asprintf(emsabp_ctx->mem_ctx, "CN=Servers,CN=First Administrative Group,CN=Administrative Groups,%s",
256 ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->conf_ctx, dn);
258 if (!ldb_dn_validate(ldb_dn)) {
262 ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn,
263 scope, recipient_attrs, "(cn=%s)", netbiosname);
264 if (ret != LDB_SUCCESS || !res->count) {
268 /* Step 3. Retrieve the objectGUID GUID */
269 guid_str = ldb_msg_find_attr_as_string(res->msgs[0], "objectGUID", NULL);
270 if (!guid_str) return NULL;
272 guid = talloc_zero(emsabp_ctx->mem_ctx, struct GUID);
273 GUID_from_string(guid_str, guid);
280 \details Build an EphemeralEntryID structure
282 \param emsabp_ctx pointer to the EMSABP context
283 \param DisplayType the AB object display type
284 \param MId the MId value
285 \param ephEntryID pointer to the EphemeralEntryID returned by the
288 \return MAPI_E_SUCCESS on success, otherwise
289 MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
291 _PUBLIC_ enum MAPISTATUS emsabp_set_EphemeralEntryID(struct emsabp_context *emsabp_ctx,
292 uint32_t DisplayType, uint32_t MId,
293 struct EphemeralEntryID *ephEntryID)
295 struct GUID *guid = (struct GUID *) NULL;
298 OPENCHANGE_RETVAL_IF(!ephEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
300 guid = emsabp_get_server_GUID(emsabp_ctx);
301 OPENCHANGE_RETVAL_IF(!guid, MAPI_E_CORRUPT_STORE, NULL);
303 ephEntryID->ID_type = 0x87;
304 ephEntryID->R1 = 0x0;
305 ephEntryID->R2 = 0x0;
306 ephEntryID->R3 = 0x0;
307 ephEntryID->ProviderUID.ab[0] = (guid->time_low & 0xFF);
308 ephEntryID->ProviderUID.ab[1] = ((guid->time_low >> 8) & 0xFF);
309 ephEntryID->ProviderUID.ab[2] = ((guid->time_low >> 16) & 0xFF);
310 ephEntryID->ProviderUID.ab[3] = ((guid->time_low >> 24) & 0xFF);
311 ephEntryID->ProviderUID.ab[4] = (guid->time_mid & 0xFF);
312 ephEntryID->ProviderUID.ab[5] = ((guid->time_mid >> 8) & 0xFF);
313 ephEntryID->ProviderUID.ab[6] = (guid->time_hi_and_version & 0xFF);
314 ephEntryID->ProviderUID.ab[7] = ((guid->time_hi_and_version >> 8) & 0xFF);
315 memcpy(ephEntryID->ProviderUID.ab + 8, guid->clock_seq, sizeof (uint8_t) * 2);
316 memcpy(ephEntryID->ProviderUID.ab + 10, guid->node, sizeof (uint8_t) * 6);
317 ephEntryID->R4 = 0x1;
318 ephEntryID->DisplayType = DisplayType;
319 ephEntryID->MId = MId;
323 return MAPI_E_SUCCESS;
328 \details Map an EphemeralEntryID structure into a Binary_r structure
330 \param mem_ctx pointer to the memory context
331 \param ephEntryID pointer to the Ephemeral EntryID structure
332 \param bin pointer to the Binary_r structure the server will return
334 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_INVALID_PARAMETER
336 _PUBLIC_ enum MAPISTATUS emsabp_EphemeralEntryID_to_Binary_r(TALLOC_CTX *mem_ctx,
337 struct EphemeralEntryID *ephEntryID,
338 struct Binary_r *bin)
341 OPENCHANGE_RETVAL_IF(!ephEntryID, MAPI_E_INVALID_PARAMETER, NULL);
342 OPENCHANGE_RETVAL_IF(!bin, MAPI_E_INVALID_PARAMETER, NULL);
344 bin->cb = sizeof (*ephEntryID);
345 bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
347 /* Copy EphemeralEntryID into bin->lpb */
348 memset(bin->lpb, 0, bin->cb);
349 bin->lpb[0] = ephEntryID->ID_type;
350 bin->lpb[1] = ephEntryID->R1;
351 bin->lpb[2] = ephEntryID->R2;
352 bin->lpb[3] = ephEntryID->R3;
353 memcpy(bin->lpb + 4, ephEntryID->ProviderUID.ab, 16);
354 bin->lpb[20] = (ephEntryID->R4 & 0xFF);
355 bin->lpb[21] = ((ephEntryID->R4 >> 8) & 0xFF);
356 bin->lpb[22] = ((ephEntryID->R4 >> 16) & 0xFF);
357 bin->lpb[23] = ((ephEntryID->R4 >> 24) & 0xFF);
358 bin->lpb[24] = (ephEntryID->DisplayType & 0xFF);
359 bin->lpb[25] = ((ephEntryID->DisplayType >> 8) & 0xFF);
360 bin->lpb[26] = ((ephEntryID->DisplayType >> 16) & 0xFF);
361 bin->lpb[27] = ((ephEntryID->DisplayType >> 24) & 0xFF);
362 bin->lpb[28] = (ephEntryID->MId & 0xFF);
363 bin->lpb[29] = ((ephEntryID->MId >> 8) & 0xFF);
364 bin->lpb[30] = ((ephEntryID->MId >> 16) & 0xFF);
365 bin->lpb[31] = ((ephEntryID->MId >> 24) & 0xFF);
367 return MAPI_E_SUCCESS;
372 \details Build a PermanentEntryID structure
374 \param emsabp_ctx pointer to the EMSABP context
375 \param DisplayType the AB object display type
376 \param ldb_recipient pointer on the LDB message
377 \param permEntryID pointer to the PermanentEntryID returned by the
380 \return MAPI_E_SUCCESS on success, otherwise
381 MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
383 _PUBLIC_ enum MAPISTATUS emsabp_set_PermanentEntryID(struct emsabp_context *emsabp_ctx,
384 uint32_t DisplayType, struct ldb_message *msg,
385 struct PermanentEntryID *permEntryID)
387 struct GUID *guid = (struct GUID *) NULL;
388 const char *guid_str;
391 OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
394 permEntryID->ID_type = 0x0;
395 permEntryID->R1 = 0x0;
396 permEntryID->R2 = 0x0;
397 permEntryID->R3 = 0x0;
398 memcpy(permEntryID->ProviderUID.ab, GUID_NSPI, 16);
399 permEntryID->R4 = 0x1;
400 permEntryID->DisplayType = DisplayType;
403 permEntryID->dn = talloc_strdup(emsabp_ctx->mem_ctx, "/");
405 guid_str = ldb_msg_find_attr_as_string(msg, "objectGUID", NULL);
406 OPENCHANGE_RETVAL_IF(!guid_str, MAPI_E_CORRUPT_STORE, NULL);
407 guid = talloc_zero(emsabp_ctx->mem_ctx, struct GUID);
408 GUID_from_string(guid_str, guid);
409 permEntryID->dn = talloc_asprintf(emsabp_ctx->mem_ctx, EMSABP_DN,
410 guid->time_low, guid->time_mid,
411 guid->time_hi_and_version,
414 guid->node[0], guid->node[1],
415 guid->node[2], guid->node[3],
416 guid->node[4], guid->node[5]);
420 return MAPI_E_SUCCESS;
425 \details Map a PermanentEntryID structure into a Binary_r
426 structure (for PR_ENTRYID and PR_EMS_AB_PARENT_ENTRYID properties)
428 \param mem_ctx pointer to the memory context
429 \param permEntryID pointer to the Permanent EntryID structure
430 \param bin pointer to the Binary_r structure the server will return
432 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_INVALID_PARAMETER
434 _PUBLIC_ enum MAPISTATUS emsabp_PermanentEntryID_to_Binary_r(TALLOC_CTX *mem_ctx,
435 struct PermanentEntryID *permEntryID,
436 struct Binary_r *bin)
439 OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_INVALID_PARAMETER, NULL);
440 OPENCHANGE_RETVAL_IF(!bin, MAPI_E_INVALID_PARAMETER, NULL);
442 /* Remove const char * size and replace it with effective dn string length */
443 bin->cb = sizeof (*permEntryID) - 4 + strlen(permEntryID->dn) + 1;
444 bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
446 /* Copy PermanantEntryID intro bin->lpb */
447 memset(bin->lpb, 0, bin->cb);
448 bin->lpb[0] = permEntryID->ID_type;
449 bin->lpb[1] = permEntryID->R1;
450 bin->lpb[2] = permEntryID->R2;
451 bin->lpb[3] = permEntryID->R3;
452 memcpy(bin->lpb + 4, permEntryID->ProviderUID.ab, 16);
453 bin->lpb[20] = (permEntryID->R4 & 0xFF);
454 bin->lpb[21] = ((permEntryID->R4 >> 8) & 0xFF);
455 bin->lpb[22] = ((permEntryID->R4 >> 16) & 0xFF);
456 bin->lpb[23] = ((permEntryID->R4 >> 24) & 0xFF);
457 bin->lpb[24] = (permEntryID->DisplayType & 0xFF);
458 bin->lpb[25] = ((permEntryID->DisplayType >> 8) & 0xFF);
459 bin->lpb[26] = ((permEntryID->DisplayType >> 16) & 0xFF);
460 bin->lpb[27] = ((permEntryID->DisplayType >> 24) & 0xFF);
461 memcpy(bin->lpb + 28, permEntryID->dn, strlen(permEntryID->dn) + 1);
463 return MAPI_E_SUCCESS;
468 \details Find the attribute matching the specified property tag and
469 return the associated data.
471 \param mem_ctx pointer to the memory context
472 \param emsabp_ctx pointer to the EMSABP context
473 \param msg pointer to the LDB message
474 \param ulPropTag the property tag to lookup
475 \param MId Minimal Entry ID associated to the current message
477 \note This implementation is at the moment limited to MAILUSER,
478 which means we arbitrary set PR_OBJECT_TYPE and PR_DISPLAY_TYPE
479 while we should have a generic method to fill these properties.
481 \return Valid generic pointer on success, otherwise NULL
483 _PUBLIC_ void *emsabp_query(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
484 struct ldb_message *msg, uint32_t ulPropTag, uint32_t MId)
486 enum MAPISTATUS retval;
487 void *data = (void *) NULL;
488 const char *attribute;
489 const char *ref_attribute;
490 const char *ldb_string = NULL;
491 struct Binary_r *bin;
492 struct StringArray_r *mvszA;
493 struct EphemeralEntryID ephEntryID;
494 struct ldb_message *msg2 = NULL;
495 struct ldb_message_element *ldb_element;
497 const char *dn = NULL;
500 /* Step 1. Fill attributes not in AD but created using EMSABP databases */
503 data = (void *) talloc_strdup(mem_ctx, EMSABP_ADDRTYPE);
506 data = talloc_zero(mem_ctx, uint32_t);
507 *((uint32_t *)data) = MAPI_MAILUSER;
509 case PR_DISPLAY_TYPE:
510 data = talloc_zero(mem_ctx, uint32_t);
511 *((uint32_t *)data) = DT_MAILUSER;
514 bin = talloc(mem_ctx, struct Binary_r);
515 retval = emsabp_set_EphemeralEntryID(emsabp_ctx, DT_MAILUSER, MId, &ephEntryID);
516 retval = emsabp_EphemeralEntryID_to_Binary_r(mem_ctx, &ephEntryID, bin);
518 case PR_INSTANCE_KEY:
519 bin = talloc_zero(mem_ctx, struct Binary_r);
521 bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
522 memset(bin->lpb, 0, bin->cb);
523 bin->lpb[0] = MId & 0xFF;
524 bin->lpb[1] = (MId >> 8) & 0xFF;
525 bin->lpb[2] = (MId >> 16) & 0xFF;
526 bin->lpb[3] = (MId >> 24) & 0xFF;
532 /* Step 2. Retrieve the attribute name associated to ulPropTag and handle the ref case */
533 attribute = emsabp_property_get_attribute(ulPropTag);
534 if (!attribute) return NULL;
536 ret = emsabp_property_is_ref(ulPropTag);
538 ref_attribute = emsabp_property_get_ref_attr(ulPropTag);
540 dn = ldb_msg_find_attr_as_string(msg, attribute, NULL);
541 retval = emsabp_search_dn(emsabp_ctx, dn, &msg2);
542 if (retval != MAPI_E_SUCCESS) {
545 attribute = ref_attribute;
551 /* Step 3. Retrieve data associated to the attribute/tag */
552 switch (ulPropTag & 0xFFFF) {
555 ldb_string = ldb_msg_find_attr_as_string(msg2, attribute, NULL);
556 if (!ldb_string) return NULL;
557 data = talloc_strdup(mem_ctx, ldb_string);
560 ldb_element = ldb_msg_find_element(msg2, attribute);
561 if (!ldb_element) return NULL;
563 mvszA = talloc(mem_ctx, struct StringArray_r);
564 mvszA->cValues = ldb_element[0].num_values & 0xFFFFFFFF;
565 mvszA->lppszA = talloc_array(mem_ctx, const char *, mvszA->cValues);
566 for (i = 0; i < mvszA->cValues; i++) {
567 mvszA->lppszA[i] = talloc_strdup(mem_ctx, (char *)ldb_element->values[i].data);
569 data = (void *) mvszA;
572 DEBUG(3, ("[%s:%d]: Unsupported property type: 0x%x\n", __FUNCTION__, __LINE__,
573 (ulPropTag & 0xFFFF)));
582 \details Builds the SRow array entry for the specified MId.
584 The function retrieves the DN associated to the specified MId
585 within its on-memory TDB database. It next fetches the LDB record
586 matching the DN and finally retrieve the requested properties for
589 \param mem_ctx pointer to the memory context
590 \param emsabp_ctx pointer to the EMSABP context
591 \param aRow pointer to the SRow structure where results will be
593 \param MId MId to fetch properties for
594 \param pPropTags pointer to the property tags array
596 \note We currently assume records are users.ldb
598 \return MAPI_E_SUCCESS on success, otherwise MAPI error
600 _PUBLIC_ enum MAPISTATUS emsabp_fetch_attrs(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
601 struct SRow *aRow, uint32_t MId,
602 struct SPropTagArray *pPropTags)
604 enum MAPISTATUS retval;
607 const char * const recipient_attrs[] = { "*", NULL };
608 struct ldb_result *res = NULL;
609 struct ldb_dn *ldb_dn = NULL;
615 /* Step 0. Try to Retrieve the dn associated to the MId first from temp TDB (users) */
616 ldb_ctx = emsabp_ctx->users_ctx;
617 retval = emsabp_tdb_fetch_dn_from_MId(mem_ctx, emsabp_ctx->ttdb_ctx, MId, &dn);
619 /* If it fails try to retrieve it from the on-disk TDB database (conf) */
620 retval = emsabp_tdb_fetch_dn_from_MId(mem_ctx, emsabp_ctx->tdb_ctx, MId, &dn);
621 ldb_ctx = emsabp_ctx->conf_ctx;
623 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
625 /* Step 1. Fetch the LDB record */
626 ldb_dn = ldb_dn_new(mem_ctx, ldb_ctx, dn);
627 OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, NULL);
629 ret = ldb_search(ldb_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn, LDB_SCOPE_BASE,
630 recipient_attrs, NULL);
631 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, NULL);
633 /* Step 2. Retrieve property values and build aRow */
634 aRow->ulAdrEntryPad = 0x0;
635 aRow->cValues = pPropTags->cValues;
636 aRow->lpProps = talloc_array(mem_ctx, struct SPropValue, aRow->cValues);
638 for (i = 0; i < aRow->cValues; i++) {
639 ulPropTag = pPropTags->aulPropTag[i];
640 data = emsabp_query(mem_ctx, emsabp_ctx, res->msgs[0], ulPropTag, MId);
642 ulPropTag &= 0xFFFF0000;
643 ulPropTag += PT_ERROR;
646 aRow->lpProps[i].ulPropTag = ulPropTag;
647 aRow->lpProps[i].dwAlignPad = 0x0;
648 set_SPropValue(&(aRow->lpProps[i]), data);
652 return MAPI_E_SUCCESS;
657 \details Builds the SRow array entry for the specified table
660 \param mem_ctx pointer to the memory context
661 \param emsabp_ctx pointer to the EMSABP context
662 \param aRow pointer to the SRow structure where results will be
664 \param dwFlags flags controlling whether strings should be unicode
666 \param permEntryID pointer to the current record Permanent
668 \param parentPermEntryID pointer to the parent record Permanent
670 \param msg pointer to the LDB message for current record
671 \param child boolean value specifying whether current record is
672 root or child within containers hierarchy
674 \return MAPI_E_SUCCESS on success, otherwise MAPI error
676 _PUBLIC_ enum MAPISTATUS emsabp_table_fetch_attrs(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
677 struct SRow *aRow, uint32_t dwFlags,
678 struct PermanentEntryID *permEntryID,
679 struct PermanentEntryID *parentPermEntryID,
680 struct ldb_message *msg, bool child)
682 enum MAPISTATUS retval;
683 struct SPropTagArray *SPropTagArray;
684 struct SPropValue lpProps;
686 uint32_t containerID = 0;
687 const char *dn = NULL;
689 /* Step 1. Build the array of properties to fetch and map */
690 if (child == false) {
691 SPropTagArray = set_SPropTagArray(mem_ctx, 0x6,
695 PR_EMS_AB_CONTAINERID,
696 ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
697 PR_EMS_AB_IS_MASTER);
699 SPropTagArray = set_SPropTagArray(mem_ctx, 0x7,
703 PR_EMS_AB_CONTAINERID,
704 ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
706 PR_EMS_AB_PARENT_ENTRYID);
709 /* Step 2. Allocate SPropValue array and update SRow cValues field */
710 aRow->ulAdrEntryPad = 0x0;
712 aRow->lpProps = talloc_zero(mem_ctx, struct SPropValue);
714 /* Step 3. Global Address List or real container */
716 /* Global Address List record is constant */
717 for (i = 0; i < SPropTagArray->cValues; i++) {
718 lpProps.ulPropTag = SPropTagArray->aulPropTag[i];
719 lpProps.dwAlignPad = 0x0;
721 switch (SPropTagArray->aulPropTag[i]) {
723 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
725 case PR_CONTAINER_FLAGS:
726 lpProps.value.l = AB_RECIPIENTS | AB_UNMODIFIABLE;
729 lpProps.value.l = 0x0;
731 case PR_EMS_AB_CONTAINERID:
732 lpProps.value.l = 0x0;
734 case PR_DISPLAY_NAME:
735 lpProps.value.lpszA = NULL;
737 case PR_DISPLAY_NAME_UNICODE:
738 lpProps.value.lpszW = NULL;
740 case PR_EMS_AB_IS_MASTER:
741 lpProps.value.b = false;
746 SRow_addprop(aRow, lpProps);
747 /* SRow_addprop internals overwrite with MAPI_E_NOT_FOUND when data is NULL */
748 if (SPropTagArray->aulPropTag[i] == PR_DISPLAY_NAME ||
749 SPropTagArray->aulPropTag[i] == PR_DISPLAY_NAME_UNICODE) {
750 aRow->lpProps[aRow->cValues - 1].value.lpszA = NULL;
751 aRow->lpProps[aRow->cValues - 1].value.lpszW = NULL;
755 for (i = 0; i < SPropTagArray->cValues; i++) {
756 lpProps.ulPropTag = SPropTagArray->aulPropTag[i];
757 lpProps.dwAlignPad = 0x0;
759 switch (SPropTagArray->aulPropTag[i]) {
761 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
763 case PR_CONTAINER_FLAGS:
766 lpProps.value.l = AB_RECIPIENTS | AB_SUBCONTAINERS | AB_UNMODIFIABLE;
769 lpProps.value.l = AB_RECIPIENTS | AB_UNMODIFIABLE;
775 lpProps.value.l = 0x1;
778 lpProps.value.l = 0x0;
782 case PR_EMS_AB_CONTAINERID:
783 dn = ldb_msg_find_attr_as_string(msg, "distinguishedName", NULL);
784 retval = emsabp_tdb_fetch_MId(emsabp_ctx->tdb_ctx, dn, &containerID);
786 retval = emsabp_tdb_insert(emsabp_ctx->tdb_ctx, dn);
787 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
788 retval = emsabp_tdb_fetch_MId(emsabp_ctx->tdb_ctx, dn, &containerID);
789 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
791 lpProps.value.l = containerID;
793 case PR_DISPLAY_NAME:
794 lpProps.value.lpszA = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "displayName", NULL));
795 if (!lpProps.value.lpszA) {
796 lpProps.ulPropTag &= 0xFFFF0000;
797 lpProps.ulPropTag += PT_ERROR;
800 case PR_DISPLAY_NAME_UNICODE:
801 lpProps.value.lpszW = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "displayName", NULL));
802 if (!lpProps.value.lpszW) {
803 lpProps.ulPropTag &= 0xFFFF0000;
804 lpProps.ulPropTag += PT_ERROR;
807 case PR_EMS_AB_IS_MASTER:
808 /* FIXME: harcoded value - no load balancing */
809 lpProps.value.l = 0x0;
811 case PR_EMS_AB_PARENT_ENTRYID:
812 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, parentPermEntryID, &lpProps.value.bin);
817 SRow_addprop(aRow, lpProps);
821 return MAPI_E_SUCCESS;
826 \details Retrieve and build the HierarchyTable requested by
827 GetSpecialTable NSPI call
829 \param mem_ctx pointer to the memory context
830 \param emsabp_ctx pointer to the EMSABP context
831 \param dwFlags flags controlling whether strings should be UNICODE
833 \param SRowSet pointer on pointer to the output SRowSet array
835 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE
837 _PUBLIC_ enum MAPISTATUS emsabp_get_HierarchyTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
838 uint32_t dwFlags, struct SRowSet **SRowSet)
840 enum MAPISTATUS retval;
842 struct PermanentEntryID gal;
843 struct PermanentEntryID parentPermEntryID;
844 struct PermanentEntryID permEntryID;
845 enum ldb_scope scope = LDB_SCOPE_SUBTREE;
846 struct ldb_request *req;
847 struct ldb_result *res = NULL;
849 struct ldb_dn *ldb_dn = NULL;
850 struct ldb_control **controls;
851 const char * const recipient_attrs[] = { "*", NULL };
852 const char *control_strings[2] = { "server_sort:0:0:displayName", NULL };
853 const char *addressBookRoots;
858 /* Step 1. Build the 'Global Address List' object using PermanentEntryID */
859 aRow = talloc_zero(mem_ctx, struct SRow);
860 OPENCHANGE_RETVAL_IF(!aRow, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
863 retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, NULL, &gal);
864 OPENCHANGE_RETVAL_IF(retval, retval, aRow);
866 retval = emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &gal, NULL, NULL, false);
869 /* Step 2. Retrieve the object pointed by addressBookRoots attribute: 'All Address Lists' */
870 ldb_filter = talloc_strdup(emsabp_ctx->mem_ctx, "(addressBookRoots=*)");
871 ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res,
872 ldb_get_default_basedn(emsabp_ctx->conf_ctx),
873 scope, recipient_attrs, ldb_filter);
874 talloc_free(ldb_filter);
875 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count, MAPI_E_CORRUPT_STORE, aRow);
877 addressBookRoots = ldb_msg_find_attr_as_string(res->msgs[0], "addressBookRoots", NULL);
878 OPENCHANGE_RETVAL_IF(!addressBookRoots, MAPI_E_CORRUPT_STORE, aRow);
881 ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->conf_ctx, addressBookRoots);
882 OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, aRow);
884 scope = LDB_SCOPE_BASE;
885 ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn,
886 scope, recipient_attrs, NULL);
887 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, aRow);
889 aRow = talloc_realloc(mem_ctx, aRow, struct SRow, aRow_idx + 1);
890 retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, res->msgs[0], &parentPermEntryID);
891 emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &parentPermEntryID, NULL, res->msgs[0], false);
895 /* Step 3. Retrieve 'All Address Lists' subcontainers */
896 res = talloc_zero(mem_ctx, struct ldb_result);
897 OPENCHANGE_RETVAL_IF(!res, MAPI_E_NOT_ENOUGH_RESOURCES, aRow);
899 controls = ldb_parse_control_strings(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, control_strings);
900 ret = ldb_build_search_req(&req, emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx,
901 ldb_dn, LDB_SCOPE_SUBTREE, "(purportedSearch=*)",
902 recipient_attrs, controls, res, ldb_search_default_callback, NULL);
904 if (ret != LDB_SUCCESS) {
906 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS, MAPI_E_CORRUPT_STORE, aRow);
909 ret = ldb_request(emsabp_ctx->conf_ctx, req);
910 if (ret == LDB_SUCCESS) {
911 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
915 if (ret != LDB_SUCCESS || !res->count) {
917 OPENCHANGE_RETVAL_IF(1, MAPI_E_CORRUPT_STORE, aRow);
920 aRow = talloc_realloc(mem_ctx, aRow, struct SRow, aRow_idx + res->count + 1);
922 for (i = 0; res->msgs[i]; i++) {
923 retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, res->msgs[i], &permEntryID);
924 emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &permEntryID, &parentPermEntryID, res->msgs[i], true);
925 talloc_free(permEntryID.dn);
926 memset(&permEntryID, 0, sizeof (permEntryID));
930 talloc_free(parentPermEntryID.dn);
932 /* Step 4. Build output SRowSet */
933 SRowSet[0]->cRows = aRow_idx;
934 SRowSet[0]->aRow = aRow;
936 return MAPI_E_SUCCESS;
941 \details Retrieve and build the CreationTemplates Table requested
942 by GetSpecialTable NSPI call
944 \param mem_ctx pointer to the memory context
945 \param emsabp_ctx pointer to the EMSABP context
946 \param dwFlags flags controlling whether strings should be UNICODE
948 \param SRowSet pointer on pointer to the output SRowSet array
950 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE
952 _PUBLIC_ enum MAPISTATUS emsabp_get_CreationTemplatesTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
953 uint32_t dwFlags, struct SRowSet **SRowSet)
955 return MAPI_E_SUCCESS;
960 \details Search Active Directory given input search criterias. The
961 function associates for each records returned by the search a
962 unique session Minimal Entry ID and a LDB message.
964 \param mem_ctx pointer to the memory context
965 \param emsabp_ctx pointer to the EMSABP context
966 \param MIds pointer to the list of MIds the function returns
967 \param Restriction pointer to restriction rules to apply to the
969 \param pStat pointer the STAT structure associated to the search
970 param limit the limit number of results the function can return
972 \note SortTypePhoneticDisplayName sort type is currently not supported.
974 \return MAPI_E_SUCCESS on success, otherwise MAPI error
976 _PUBLIC_ enum MAPISTATUS emsabp_search(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
977 struct SPropTagArray *MIds, struct Restriction_r *restriction,
978 struct STAT *pStat, uint32_t limit)
980 enum MAPISTATUS retval;
981 struct ldb_result *res = NULL;
982 struct PropertyRestriction_r *res_prop = NULL;
983 const char *recipient = NULL;
984 const char * const recipient_attrs[] = { "*", NULL };
990 /* Step 0. Sanity Checks (MS-NSPI Server Processing Rules) */
991 if (pStat->SortType == SortTypePhoneticDisplayName) {
992 return MAPI_E_CALL_FAILED;
995 if (((pStat->SortType == SortTypeDisplayName) || (pStat->SortType == SortTypePhoneticDisplayName)) &&
996 (pStat->ContainerID && (emsabp_tdb_lookup_MId(emsabp_ctx->tdb_ctx, pStat->ContainerID) == false))) {
997 return MAPI_E_INVALID_BOOKMARK;
1000 if (restriction && (pStat->SortType != SortTypeDisplayName) &&
1001 (pStat->SortType != SortTypePhoneticDisplayName)) {
1002 return MAPI_E_CALL_FAILED;
1005 /* Step 1. Apply restriction and retrieve results from AD */
1007 /* FIXME: We only support RES_PROPERTY restriction */
1008 if ((uint32_t)restriction->rt != RES_PROPERTY) {
1009 return MAPI_E_TOO_COMPLEX;
1012 /* FIXME: We only support PR_ANR */
1013 res_prop = (struct PropertyRestriction_r *)&(restriction->res);
1014 if ((res_prop->ulPropTag != PR_ANR) && (res_prop->ulPropTag != PR_ANR_UNICODE)) {
1015 return MAPI_E_NO_SUPPORT;
1018 recipient = (res_prop->ulPropTag == PR_ANR) ?
1019 res_prop->lpProp->value.lpszA :
1020 res_prop->lpProp->value.lpszW;
1022 ldb_filter = talloc_asprintf(emsabp_ctx->mem_ctx, "(&(objectClass=user)(sAMAccountName=*%s*)(!(objectClass=computer)))", recipient);
1023 ret = ldb_search(emsabp_ctx->users_ctx, emsabp_ctx->mem_ctx, &res,
1024 ldb_get_default_basedn(emsabp_ctx->users_ctx),
1025 LDB_SCOPE_SUBTREE, recipient_attrs, ldb_filter);
1026 talloc_free(ldb_filter);
1028 if (ret != LDB_SUCCESS || !res->count) {
1029 return MAPI_E_NOT_FOUND;
1032 /* FIXME Check restriction == NULL */
1035 if (limit && res->count > limit) {
1036 return MAPI_E_TABLE_TOO_BIG;
1039 MIds->aulPropTag = talloc_array(emsabp_ctx->mem_ctx, uint32_t, res->count);
1040 MIds->cValues = res->count;
1042 /* Step 2. Create session MId for all fetched records */
1043 for (i = 0; i < res->count; i++) {
1044 dn = ldb_msg_find_attr_as_string(res->msgs[i], "distinguishedName", NULL);
1045 retval = emsabp_tdb_fetch_MId(emsabp_ctx->ttdb_ctx, dn, &MIds->aulPropTag[i]);
1047 retval = emsabp_tdb_insert(emsabp_ctx->ttdb_ctx, dn);
1048 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
1049 retval = emsabp_tdb_fetch_MId(emsabp_ctx->ttdb_ctx, dn, &(MIds->aulPropTag[i]));
1050 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
1054 return MAPI_E_SUCCESS;
1059 \details Search for a given DN within AD and return the associated
1062 \param emsabp_ctx pointer to the EMSABP context
1063 \param dn pointer to the DN string to search for
1064 \param ldb_res pointer on pointer to the LDB message returned by
1067 \return MAPI_E_SUCCESS on success, otherwise MAPI error
1069 _PUBLIC_ enum MAPISTATUS emsabp_search_dn(struct emsabp_context *emsabp_ctx, const char *dn,
1070 struct ldb_message **ldb_res)
1072 struct ldb_dn *ldb_dn = NULL;
1073 struct ldb_result *res = NULL;
1074 const char * const recipient_attrs[] = { "*", NULL };
1078 OPENCHANGE_RETVAL_IF(!dn, MAPI_E_INVALID_PARAMETER, NULL);
1079 OPENCHANGE_RETVAL_IF(!ldb_res, MAPI_E_INVALID_PARAMETER, NULL);
1081 ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->conf_ctx, dn);
1082 OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, NULL);
1084 ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn,
1085 LDB_SCOPE_BASE, recipient_attrs, NULL);
1086 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, NULL);
1088 *ldb_res = res->msgs[0];
1090 return MAPI_E_SUCCESS;
1095 \details Search for a given AD record given its legacyDN parameter
1096 and return the associated LDB message.
1098 \param emsabp_ctx pointer to the EMSABP context
1099 \param legacyDN pointer to the legacyDN attribute value to lookup
1100 \param ldb_res pointer on pointer to the LDB message returned by
1103 \return MAPI_E_SUCCESS on success, otherwise MAPI error
1105 _PUBLIC_ enum MAPISTATUS emsabp_search_legacyExchangeDN(struct emsabp_context *emsabp_ctx, const char *legacyDN,
1106 struct ldb_message **ldb_res)
1109 const char * const recipient_attrs[] = { "*", NULL };
1111 struct ldb_result *res = NULL;
1114 OPENCHANGE_RETVAL_IF(!legacyDN, MAPI_E_INVALID_PARAMETER, NULL);
1115 OPENCHANGE_RETVAL_IF(!ldb_res, MAPI_E_INVALID_PARAMETER, NULL);
1117 ldb_filter = talloc_asprintf(emsabp_ctx->mem_ctx, "(legacyExchangeDN=%s)", legacyDN);
1118 ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res,
1119 ldb_get_default_basedn(emsabp_ctx->conf_ctx),
1120 LDB_SCOPE_SUBTREE, recipient_attrs, ldb_filter);
1121 talloc_free(ldb_filter);
1123 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count, MAPI_E_NOT_FOUND, NULL);
1125 *ldb_res = res->msgs[0];
1127 return MAPI_E_SUCCESS;