Merge branch 'master' into security-summit
[platform/upstream/iotivity.git] / resource / csdk / security / provisioning / src / secureresourceprovider.c
1 /* *****************************************************************
2  *
3  * Copyright 2015 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * *****************************************************************/
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdint.h>
23 #include <unistd.h>
24
25 #include "ocprovisioningmanager.h"
26 #include "secureresourceprovider.h"
27 #include "logger.h"
28 #include "oic_malloc.h"
29 #include "aclresource.h"
30 #include "pstatresource.h"
31 #include "srmresourcestrings.h"
32 #include "credresource.h"
33 #include "doxmresource.h"
34 #include "credentialgenerator.h"
35 #include "cainterface.h"
36 #include "cJSON.h"
37 #include "pmtypes.h"
38 #include "pmutility.h"
39 #include "provisioningdatabasemanager.h"
40 #include "base64.h"
41 #include "utlist.h"
42
43 #define TAG "SRPAPI"
44
45 /**
46  * Macro to verify argument is not equal to NULL.
47  * eg: VERIFY_NON_NULL(TAG, ptrData, ERROR,OC_STACK_ERROR);
48  */
49 #define VERIFY_NON_NULL(tag, arg, logLevel, retValue) { if (NULL == (arg)) \
50             { OC_LOG((logLevel), tag, (#arg " is NULL")); return retValue; } }
51
52 /**
53  * Macro to verify success of operation.
54  * eg: VERIFY_SUCCESS(TAG, OC_STACK_OK == foo(), ERROR, OC_STACK_ERROR);
55  */
56 #define VERIFY_SUCCESS(tag, op, logLevel, retValue) { if (!(op)) \
57             {OC_LOG((logLevel), tag, (#op " failed!!")); return retValue;} }
58
59 /**
60  * Structure to carry credential data to callback.
61  */
62 typedef struct CredentialData CredentialData_t;
63 struct CredentialData
64 {
65     void *ctx;                                  /**< Pointer to user context.**/
66     const OCProvisionDev_t *deviceInfo1;        /**< Pointer to OCProvisionDev_t.**/
67     const OCProvisionDev_t *deviceInfo2;        /**< Pointer to OCProvisionDev_t.**/
68     OicSecCred_t *credInfo;                     /**< Pointer to OicSecCred_t.**/
69     OicSecCred_t *credInfoFirst;                /**< Pointer to OicSecCred_t.**/
70     OCProvisionResultCB resultCallback;         /**< Pointer to result callback.**/
71     OCProvisionResult_t *resArr;                /**< Result array.**/
72     int numOfResults;                           /**< Number of results in result array.**/
73 };
74
75 /**
76  * Structure to carry ACL provision API data to callback.
77  */
78 typedef struct ACLData ACLData_t;
79 struct ACLData
80 {
81     void *ctx;                                  /**< Pointer to user context.**/
82     const OCProvisionDev_t *deviceInfo;         /**< Pointer to PMDevInfo_t.**/
83     OCProvisionResultCB resultCallback;         /**< Pointer to result callback.**/
84     OCProvisionResult_t *resArr;                /**< Result array.**/
85     int numOfResults;                           /**< Number of results in result array.**/
86 };
87
88 // Enum type index for unlink callback.
89 typedef enum {
90     IDX_FIRST_DEVICE_RES = 0, // index for resulf of the first device
91     IDX_SECOND_DEVICE_RES,    // index for result of the second device
92     IDX_DB_UPDATE_RES         // index for result of updating provisioning database.
93 } IdxUnlinkRes_t;
94
95 // Structure to carry unlink APIs data to callback.
96 typedef struct UnlinkData UnlinkData_t;
97 struct UnlinkData {
98     void *ctx;
99     OCProvisionDev_t* unlinkDev;             /**< Pointer to OCProvisionDev_t to be unlinked.**/
100     OCProvisionResult_t* unlinkRes;          /**< Result array.**/
101     OCProvisionResultCB resultCallback;      /**< Pointer to result callback.**/
102     int numOfResults;                        /**< Number of results in result array.**/
103 };
104
105 //Example of DELETE cred request -> coaps://0.0.0.0:5684/oic/sec/cred?sub=(BASE64 ENCODED UUID)
106 const char * SRP_FORM_DELETE_CREDENTIAL = "coaps://[%s]:%d%s?%s=%s";
107
108 // Structure to carry remove APIs data to callback.
109 typedef struct RemoveData RemoveData_t;
110 struct RemoveData {
111     void *ctx;
112     OCProvisionDev_t* revokeTargetDev;      /**< Device which is going to be revoked..**/
113     OCProvisionDev_t* linkedDevList;        /**< A list of devices which have invalid credential.**/
114     OCProvisionResult_t* removeRes;         /**< Result array.**/
115     OCProvisionResultCB resultCallback;     /**< Pointer to result callback.**/
116     size_t numOfResults;                    /**< Number of results in result array.**/
117     size_t sizeOfResArray;
118     bool hasError;
119 };
120
121 /**
122  * Function prototype
123  */
124 static OCStackResult provisionCredentials(const OicSecCred_t *cred,
125         const OCProvisionDev_t *deviceInfo, CredentialData_t *credData,
126         OCClientResponseHandler responseHandler);
127
128
129 /**
130  * Internal function to update result in result array.
131  */
132 static void registerResultForCredProvisioning(CredentialData_t *credData,
133                                               OCStackResult stackresult, int cause)
134 {
135
136    OC_LOG_V(INFO,TAG,"value of credData->numOfResults is %d",credData->numOfResults);
137    if(1 == cause)
138    {
139        memcpy(credData->resArr[(credData->numOfResults)].deviceId.id,
140               credData->deviceInfo1->doxm->deviceID.id,UUID_LENGTH);
141    }
142    else
143    {
144        memcpy(credData->resArr[(credData->numOfResults)].deviceId.id,
145               credData->deviceInfo2->doxm->deviceID.id,UUID_LENGTH);
146    }
147    credData->resArr[(credData->numOfResults)].res = stackresult;
148    ++(credData->numOfResults);
149 }
150
151 /**
152  * Callback handler for handling callback of provisioning device 2.
153  *
154  * @param[in] ctx             ctx value passed to callback from calling function.
155  * @param[in] UNUSED          handle to an invocation
156  * @param[in] clientResponse  Response from queries to remote servers.
157  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction
158  *          and  OC_STACK_KEEP_TRANSACTION to keep it.
159  */
160 static OCStackApplicationResult provisionCredentialCB2(void *ctx, OCDoHandle UNUSED,
161                                                        OCClientResponse *clientResponse)
162 {
163     VERIFY_NON_NULL(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
164     CredentialData_t *credData = (CredentialData_t *) ctx;
165     (void)UNUSED;
166
167     OCProvisionResultCB resultCallback = credData->resultCallback;
168     OC_LOG(INFO, TAG, "provisionCredentialCB2 called");
169     if (clientResponse)
170     {
171         if(OC_STACK_RESOURCE_CREATED == clientResponse->result)
172         {
173             registerResultForCredProvisioning(credData, OC_STACK_RESOURCE_CREATED, 2);
174             OCStackResult res =  PDMLinkDevices(&credData->deviceInfo1->doxm->deviceID,
175                     &credData->deviceInfo2->doxm->deviceID);
176             if (OC_STACK_OK != res)
177             {
178                 OC_LOG(ERROR, TAG, "Error occured on PDMLinkDevices");
179                 return OC_STACK_DELETE_TRANSACTION;
180             }
181             OC_LOG(INFO, TAG, "Link created successfully");
182
183             ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
184                                                     credData->resArr,
185                                                     false);
186              OICFree(credData->resArr);
187              OICFree(credData);
188              return OC_STACK_DELETE_TRANSACTION;
189         }
190
191     }
192     OC_LOG(INFO, TAG, "provisionCredentialCB2 received Null clientResponse");
193     registerResultForCredProvisioning(credData, OC_STACK_ERROR, 2);
194     ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
195                                             credData->resArr,
196                                             true);
197     OICFree(credData->resArr);
198     OICFree(credData);
199     return OC_STACK_DELETE_TRANSACTION;
200 }
201
202 /**
203  * Callback handler for handling callback of provisioning device 1.
204  *
205  * @param[in] ctx             ctx value passed to callback from calling function.
206  * @param[in] UNUSED          handle to an invocation
207  * @param[in] clientResponse  Response from queries to remote servers.
208  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction
209  *          and  OC_STACK_KEEP_TRANSACTION to keep it.
210  */
211 static OCStackApplicationResult provisionCredentialCB1(void *ctx, OCDoHandle UNUSED,
212                                                        OCClientResponse *clientResponse)
213 {
214     VERIFY_NON_NULL(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
215     (void)UNUSED;
216     CredentialData_t* credData = (CredentialData_t*) ctx;
217     OICFree(credData->credInfoFirst);
218     const OCProvisionDev_t *deviceInfo = credData->deviceInfo2;
219     OicSecCred_t *credInfo = credData->credInfo;
220     const OCProvisionResultCB resultCallback = credData->resultCallback;
221     if (clientResponse)
222     {
223         if (OC_STACK_RESOURCE_CREATED == clientResponse->result)
224         {
225             // send credentials to second device
226             registerResultForCredProvisioning(credData, OC_STACK_RESOURCE_CREATED,1);
227             OCStackResult res = provisionCredentials(credInfo, deviceInfo, credData,
228                     provisionCredentialCB2);
229             DeleteCredList(credInfo);
230             if (OC_STACK_OK != res)
231             {
232                 registerResultForCredProvisioning(credData, res,2);
233                 ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
234                                                         credData->resArr,
235                                                         true);
236                 OICFree(credData->resArr);
237                 OICFree(credData);
238                 credData = NULL;
239             }
240         }
241         else
242         {
243             registerResultForCredProvisioning(credData, OC_STACK_ERROR,1);
244             ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
245                                                     credData->resArr,
246                                                     true);
247             OICFree(credData->resArr);
248             OICFree(credData);
249             credData = NULL;
250         }
251     }
252     else
253     {
254         OC_LOG(INFO, TAG, "provisionCredentialCB received Null clientResponse for first device");
255         registerResultForCredProvisioning(credData, OC_STACK_ERROR,1);
256        ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
257                                                      credData->resArr,
258                                                      true);
259         DeleteCredList(credInfo);
260         OICFree(credData->resArr);
261         OICFree(credData);
262         credData = NULL;
263     }
264     return OC_STACK_DELETE_TRANSACTION;
265 }
266
267
268
269 /**
270  * Internal function for handling credential generation and sending credential to resource server.
271  *
272  * @param[in] cred Instance of cred resource.
273  * @param[in] deviceInfo information about device to which credential is to be provisioned.
274  * @param[in] responseHandler callbak called by OC stack when request API receives response.
275  * @return  OC_STACK_OK in case of success and other value otherwise.
276  */
277 static OCStackResult provisionCredentials(const OicSecCred_t *cred,
278         const OCProvisionDev_t *deviceInfo, CredentialData_t *credData,
279         OCClientResponseHandler responseHandler)
280 {
281     OCSecurityPayload* secPayload = (OCSecurityPayload*)OICCalloc(1, sizeof(OCSecurityPayload));
282     if(!secPayload)
283     {
284         OC_LOG(ERROR, TAG, "Failed to memory allocation");
285         return OC_STACK_NO_MEMORY;
286     }
287     secPayload->base.type = PAYLOAD_TYPE_SECURITY;
288     secPayload->securityData = BinToCredJSON(cred);
289     if(NULL == secPayload->securityData)
290     {
291         OICFree(secPayload);
292         OC_LOG(ERROR, TAG, "Failed to BinToCredJSON");
293         return OC_STACK_NO_MEMORY;
294     }
295
296     OC_LOG_V(INFO, TAG, "Credential for provisioning : %s",secPayload->securityData);
297     char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
298     if(!PMGenerateQuery(true,
299                         deviceInfo->endpoint.addr,
300                         deviceInfo->securePort,
301                         deviceInfo->connType,
302                         query, sizeof(query), OIC_RSRC_CRED_URI))
303     {
304         OC_LOG(ERROR, TAG, "DeviceDiscoveryHandler : Failed to generate query");
305         return OC_STACK_ERROR;
306     }
307     OC_LOG_V(DEBUG, TAG, "Query=%s", query);
308
309     OCCallbackData cbData = {.context=NULL, .cb=NULL, .cd=NULL};
310     cbData.cb = responseHandler;
311     cbData.context = (void *) credData;
312     cbData.cd = NULL;
313
314     OCDoHandle handle = NULL;
315     OCMethod method = OC_REST_POST;
316     OCStackResult ret = OCDoResource(&handle, method, query, 0, (OCPayload*)secPayload,
317             deviceInfo->connType, OC_HIGH_QOS, &cbData, NULL, 0);
318     OC_LOG_V(INFO, TAG, "OCDoResource::Credential provisioning returned : %d",ret);
319     if (ret != OC_STACK_OK)
320     {
321         OC_LOG(ERROR, TAG, "OCStack resource error");
322         return ret;
323     }
324     return OC_STACK_OK;
325 }
326
327 OCStackResult SRPProvisionCredentials(void *ctx, OicSecCredType_t type, size_t keySize,
328                                       const OCProvisionDev_t *pDev1,
329                                       const OCProvisionDev_t *pDev2,
330                                       OCProvisionResultCB resultCallback)
331 {
332     VERIFY_NON_NULL(TAG, pDev1, ERROR,  OC_STACK_INVALID_PARAM);
333     VERIFY_NON_NULL(TAG, pDev2, ERROR,  OC_STACK_INVALID_PARAM);
334     VERIFY_NON_NULL(TAG, resultCallback, ERROR,  OC_STACK_INVALID_CALLBACK);
335
336     if (!(keySize == OWNER_PSK_LENGTH_128 || keySize == OWNER_PSK_LENGTH_256))
337     {
338         OC_LOG(INFO, TAG, "Invalid key size");
339         return OC_STACK_INVALID_PARAM;
340     }
341
342     OC_LOG(INFO, TAG, "In SRPProvisionCredentials");
343
344     OicUuid_t provTooldeviceID =   {{0,}};
345     if (OC_STACK_OK != GetDoxmDeviceID(&provTooldeviceID))
346     {
347         OC_LOG(ERROR, TAG, "Error while retrieving provisioning tool's device ID");
348         return OC_STACK_ERROR;
349     }
350     OC_LOG(INFO, TAG, "retrieved deviceid");
351     switch (type)
352     {
353         case SYMMETRIC_PAIR_WISE_KEY:
354         {
355             const OCProvisionDev_t *firstDevice = pDev1;
356             const OCProvisionDev_t *secondDevice = pDev2;
357
358             OicSecCred_t *firstCred = NULL;
359             OicSecCred_t *secondCred = NULL;
360             OCStackResult res = PMGeneratePairWiseCredentials(type, keySize, &provTooldeviceID,
361                     &firstDevice->doxm->deviceID, &secondDevice->doxm->deviceID,
362                     &firstCred, &secondCred);
363             VERIFY_SUCCESS(TAG, (res==OC_STACK_OK), ERROR, OC_STACK_ERROR);
364             OC_LOG(INFO, TAG, "Credentials generated successfully");
365             CredentialData_t *credData = (CredentialData_t *) OICMalloc(sizeof(CredentialData_t));
366             if (NULL == credData)
367             {
368                 OC_LOG(ERROR, TAG, "Memory allocation problem");
369                 return OC_STACK_NO_MEMORY;
370             }
371             memset(credData, 0x00, sizeof(CredentialData_t));
372             credData->deviceInfo1 = firstDevice;
373             credData->deviceInfo2 = secondDevice;
374             credData->credInfo = secondCred;
375             credData->ctx = ctx;
376             credData->credInfoFirst = firstCred;
377             credData->numOfResults = 0;
378             credData->resultCallback = resultCallback;
379             // first call to provision creds to device1.
380             // second call to provision creds to device2.
381             int noOfRiCalls = 2;
382             credData->resArr =
383                 (OCProvisionResult_t*)OICMalloc(sizeof(OCProvisionResult_t) * noOfRiCalls);
384             if (NULL == credData->resArr)
385             {
386                 OC_LOG(ERROR, TAG, "Memory allocation problem");
387                 return OC_STACK_NO_MEMORY;
388             }
389             memset(credData->resArr, 0x00, sizeof(sizeof(OCProvisionResult_t)*noOfRiCalls));
390             res = provisionCredentials(firstCred, firstDevice, credData, &provisionCredentialCB1);
391             if (OC_STACK_OK != res)
392             {
393                 DeleteCredList(firstCred);
394                 DeleteCredList(secondCred);
395                 OICFree(credData->resArr);
396                 OICFree(credData);
397             }
398             OC_LOG_V(INFO, TAG, "provisionCredentials returned: %d",res);
399             VERIFY_SUCCESS(TAG, (res==OC_STACK_OK), ERROR, OC_STACK_ERROR);
400             return res;
401         }
402         default:
403         {
404             OC_LOG(ERROR, TAG, "Invalid option.");
405             return OC_STACK_INVALID_PARAM;
406         }
407     }
408     return OC_STACK_ERROR;
409 }
410
411 /**
412  * Internal Function to store results in result array during ACL provisioning.
413  */
414 static void registerResultForACLProvisioning(ACLData_t *aclData,
415                                              OCStackResult stackresult)
416 {
417    OC_LOG_V(INFO, TAG, "Inside registerResultForACLProvisioning aclData->numOfResults is %d\n",
418                        aclData->numOfResults);
419    memcpy(aclData->resArr[(aclData->numOfResults)].deviceId.id,
420           aclData->deviceInfo->doxm->deviceID.id, UUID_LENGTH);
421    aclData->resArr[(aclData->numOfResults)].res = stackresult;
422    ++(aclData->numOfResults);
423 }
424
425 /**
426  * Callback handler of SRPProvisionACL.
427  *
428  * @param[in] ctx             ctx value passed to callback from calling function.
429  * @param[in] UNUSED          handle to an invocation
430  * @param[in] clientResponse  Response from queries to remote servers.
431  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction
432  *          and  OC_STACK_KEEP_TRANSACTION to keep it.
433  */
434 static OCStackApplicationResult SRPProvisionACLCB(void *ctx, OCDoHandle UNUSED,
435                                                   OCClientResponse *clientResponse)
436 {
437     OC_LOG_V(INFO, TAG, "Inside SRPProvisionACLCB.");
438     (void)UNUSED;
439     VERIFY_NON_NULL(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
440     ACLData_t *aclData = (ACLData_t*)ctx;
441     OCProvisionResultCB resultCallback = aclData->resultCallback;
442
443     if (clientResponse)
444     {
445         if(OC_STACK_RESOURCE_CREATED == clientResponse->result)
446         {
447             registerResultForACLProvisioning(aclData, OC_STACK_RESOURCE_CREATED);
448             ((OCProvisionResultCB)(resultCallback))(aclData->ctx, aclData->numOfResults,
449                                                     aclData->resArr,
450                                                     false);
451              OICFree(aclData->resArr);
452              OICFree(aclData);
453              return OC_STACK_DELETE_TRANSACTION;
454         }
455     }
456     registerResultForACLProvisioning(aclData, OC_STACK_ERROR);
457     ((OCProvisionResultCB)(resultCallback))(aclData->ctx, aclData->numOfResults,
458                                             aclData->resArr,
459                                             true);
460     OC_LOG_V(ERROR, TAG, "SRPProvisionACLCB received Null clientResponse");
461     OICFree(aclData->resArr);
462     OICFree(aclData);
463     return OC_STACK_DELETE_TRANSACTION;
464 }
465
466 OCStackResult SRPProvisionACL(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
467         OicSecAcl_t *acl, OCProvisionResultCB resultCallback)
468 {
469     VERIFY_NON_NULL(TAG, selectedDeviceInfo, ERROR,  OC_STACK_INVALID_PARAM);
470     VERIFY_NON_NULL(TAG, acl, ERROR,  OC_STACK_INVALID_PARAM);
471     VERIFY_NON_NULL(TAG, resultCallback, ERROR,  OC_STACK_INVALID_CALLBACK);
472
473     OCSecurityPayload* secPayload = (OCSecurityPayload*)OICCalloc(1, sizeof(OCSecurityPayload));
474     if(!secPayload)
475     {
476         OC_LOG(ERROR, TAG, "Failed to memory allocation");
477         return OC_STACK_NO_MEMORY;
478     }
479     secPayload->base.type = PAYLOAD_TYPE_SECURITY;
480     secPayload->securityData = BinToAclJSON(acl);
481     if(NULL == secPayload->securityData)
482     {
483         OICFree(secPayload);
484         OC_LOG(ERROR, TAG, "Failed to BinToAclJSON");
485         return OC_STACK_NO_MEMORY;
486     }
487     OC_LOG_V(INFO, TAG, "ACL : %s", secPayload->securityData);
488
489     char query[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
490     if(!PMGenerateQuery(true,
491                         selectedDeviceInfo->endpoint.addr,
492                         selectedDeviceInfo->securePort,
493                         selectedDeviceInfo->connType,
494                         query, sizeof(query), OIC_RSRC_ACL_URI))
495     {
496         OC_LOG(ERROR, TAG, "DeviceDiscoveryHandler : Failed to generate query");
497         return OC_STACK_ERROR;
498     }
499     OC_LOG_V(DEBUG, TAG, "Query=%s", query);
500
501     OCCallbackData cbData =  {.context=NULL, .cb=NULL, .cd=NULL};
502     cbData.cb = &SRPProvisionACLCB;
503     ACLData_t *aclData = (ACLData_t *) OICMalloc(sizeof(ACLData_t));
504     if (aclData == NULL)
505     {
506         OICFree(secPayload);
507         OC_LOG(ERROR, TAG, "Unable to allocate memory");
508         return OC_STACK_NO_MEMORY;
509     }
510     memset(aclData, 0x00, sizeof(ACLData_t));
511     aclData->deviceInfo = selectedDeviceInfo;
512     aclData->resultCallback = resultCallback;
513     aclData->numOfResults=0;
514     aclData->ctx = ctx;
515     // call to provision ACL to device1.
516     int noOfRiCalls = 1;
517     aclData->resArr = (OCProvisionResult_t*)OICMalloc(sizeof(OCProvisionResult_t)*noOfRiCalls);
518     if (aclData->resArr == NULL)
519     {
520         OICFree(secPayload);
521         OC_LOG(ERROR, TAG, "Unable to allocate memory");
522         return OC_STACK_NO_MEMORY;
523     }
524     memset(aclData->resArr, 0x00, sizeof(sizeof(OCProvisionResult_t)*noOfRiCalls));
525     cbData.context = (void *)aclData;
526     cbData.cd = NULL;
527     OCMethod method = OC_REST_POST;
528     OCDoHandle handle = NULL;
529     OC_LOG(DEBUG, TAG, "Sending ACL info to resource server");
530     OCStackResult ret = OCDoResource(&handle, method, query,
531             &selectedDeviceInfo->endpoint, (OCPayload*)secPayload,
532             selectedDeviceInfo->connType, OC_HIGH_QOS, &cbData, NULL, 0);
533     if (ret != OC_STACK_OK)
534     {
535         OICFree(aclData->resArr);
536         OICFree(aclData);
537     }
538     VERIFY_SUCCESS(TAG, (OC_STACK_OK == ret), ERROR, OC_STACK_ERROR);
539     return OC_STACK_OK;
540 }
541
542 static void DeleteUnlinkData_t(UnlinkData_t *unlinkData)
543 {
544     if (unlinkData)
545     {
546         OICFree(unlinkData->unlinkDev);
547         OICFree(unlinkData->unlinkRes);
548         OICFree(unlinkData);
549     }
550 }
551
552 static void registerResultForUnlinkDevices(UnlinkData_t *unlinkData, OCStackResult stackresult,
553                                            IdxUnlinkRes_t idx)
554 {
555     if (NULL != unlinkData)
556     {
557         OC_LOG_V(INFO, TAG, "Inside registerResultForUnlinkDevices unlinkData->numOfResults is %d\n",
558                             unlinkData->numOfResults);
559         OC_LOG_V(INFO, TAG, "Stack result :: %d", stackresult);
560
561         OicUuid_t *pUuid = &unlinkData->unlinkRes[(unlinkData->numOfResults)].deviceId;
562
563         // Set result in the result array according to the position (devNum).
564         if (idx != IDX_DB_UPDATE_RES)
565         {
566             memcpy(pUuid->id, unlinkData->unlinkDev[idx].doxm->deviceID.id, sizeof(pUuid->id));
567         }
568         else
569         {   // When deivce ID is 000... this means it's the result of database update.
570             memset(pUuid->id, 0, sizeof(pUuid->id));
571         }
572         unlinkData->unlinkRes[(unlinkData->numOfResults)].res = stackresult;
573         ++(unlinkData->numOfResults);
574         OC_LOG (INFO, TAG, "Out registerResultForUnlinkDevices");
575     }
576 }
577
578 static OCStackResult SendDeleteCredentialRequest(void* ctx,
579                                                  OCClientResponseHandler respHandler,
580                                                  const OCProvisionDev_t* revokedDev,
581                                                  const OCProvisionDev_t* destDev)
582 {
583     OC_LOG(DEBUG, TAG, "IN SendDeleteCredentialRequest");
584
585     if (NULL == ctx || NULL == respHandler || NULL == revokedDev || NULL == destDev)
586     {
587         return OC_STACK_INVALID_PARAM;
588     }
589
590     char base64Buff[B64ENCODE_OUT_SAFESIZE(sizeof(revokedDev->doxm->deviceID.id)) + 1] = {};
591     uint32_t base64Len = 0;
592     if (B64_OK != b64Encode(revokedDev->doxm->deviceID.id, sizeof(revokedDev->doxm->deviceID.id),
593                            base64Buff, sizeof(base64Buff), &base64Len))
594     {
595         OC_LOG(ERROR, TAG, "SendDeleteCredentialRequest : Failed to base64 encoding");
596         return OC_STACK_ERROR;
597     }
598
599     char reqBuf[MAX_URI_LENGTH + MAX_QUERY_LENGTH] = {0};
600     int snRet = 0;
601                     //coaps://0.0.0.0:5684/oic/sec/cred?sub=(BASE64 ENCODED UUID)
602     snRet = snprintf(reqBuf, sizeof(reqBuf), SRP_FORM_DELETE_CREDENTIAL, destDev->endpoint.addr,
603                      destDev->securePort, OIC_RSRC_CRED_URI, OIC_JSON_SUBJECT_NAME, base64Buff);
604     if (snRet < 0)
605     {
606         OC_LOG_V(ERROR, TAG, "SendDeleteCredentialRequest : Error (snprintf) %d\n", snRet);
607         return OC_STACK_ERROR;
608     }
609     else if ((size_t)snRet >= sizeof(reqBuf))
610     {
611         OC_LOG_V(ERROR, TAG, "SendDeleteCredentialRequest : Truncated (snprintf) %d\n", snRet);
612         return OC_STACK_ERROR;
613     }
614
615     OCCallbackData cbData;
616     memset(&cbData, 0, sizeof(cbData));
617     cbData.context = ctx;
618     cbData.cb = respHandler;
619     cbData.cd = NULL;
620     OC_LOG_V(INFO, TAG, "URI: %s",reqBuf);
621
622     OC_LOG(DEBUG, TAG, "Sending remove credential request to resource server");
623
624     OCStackResult ret = OCDoResource(NULL, OC_REST_DELETE, reqBuf,
625                                      &destDev->endpoint, NULL,
626                                      CT_ADAPTER_IP, OC_HIGH_QOS, &cbData, NULL, 0);
627     if (OC_STACK_OK != ret)
628     {
629         OC_LOG_V(ERROR, TAG, "SendDeleteCredentialRequest : Error in OCDoResource %d", ret);
630     }
631     OC_LOG(DEBUG, TAG, "OUT SendDeleteCredentialRequest");
632
633     return ret;
634 }
635
636 /**
637  * Callback handler of unlink second device.
638  *
639  * @param[in] ctx             ctx value passed to callback from calling function.
640  * @param[in] handle          handle to an invocation
641  * @param[in] clientResponse  Response from queries to remote servers.
642  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction and
643  *          OC_STACK_KEEP_TRANSACTION to keep it.
644  */
645 static OCStackApplicationResult SRPUnlinkDevice2CB(void *unlinkCtx, OCDoHandle handle,
646         OCClientResponse *clientResponse)
647 {
648     (void) handle;
649     OC_LOG(DEBUG, TAG, "IN SRPUnlinkDevice2CB");
650     VERIFY_NON_NULL(TAG, unlinkCtx, ERROR, OC_STACK_DELETE_TRANSACTION);
651     UnlinkData_t* unlinkData = (UnlinkData_t*)unlinkCtx;
652
653     if (clientResponse)
654     {
655         OC_LOG(DEBUG, TAG, "Valid client response for device 2");
656         registerResultForUnlinkDevices(unlinkData, clientResponse->result, IDX_SECOND_DEVICE_RES);
657
658         if (OC_STACK_RESOURCE_DELETED == clientResponse->result)
659         {
660             OC_LOG(DEBUG, TAG, "Credential of device2 revoked");
661         }
662         else
663         {
664             OC_LOG(ERROR, TAG, "Unable to delete credential information from device 2");
665             unlinkData->resultCallback(unlinkData->ctx,
666                                        unlinkData->numOfResults, unlinkData->unlinkRes, true);
667             goto error;
668         }
669     }
670     else
671     {
672         registerResultForUnlinkDevices(unlinkData, OC_STACK_INVALID_REQUEST_HANDLE,
673                                        IDX_SECOND_DEVICE_RES);
674         unlinkData->resultCallback(unlinkData->ctx,
675                                    unlinkData->numOfResults, unlinkData->unlinkRes, true);
676         OC_LOG(ERROR, TAG, "SRPUnlinkDevice2CB received Null clientResponse");
677         goto error;
678     }
679
680     //Update provisioning DB when succes case.
681     if (OC_STACK_OK != PDMUnlinkDevices(&unlinkData->unlinkDev[0].doxm->deviceID,
682                                        &unlinkData->unlinkDev[1].doxm->deviceID))
683     {
684         OC_LOG(FATAL, TAG, "All requests are successfully done but update provisioning DB FAILED.");
685         registerResultForUnlinkDevices(unlinkData, OC_STACK_INCONSISTENT_DB, IDX_DB_UPDATE_RES);
686         unlinkData->resultCallback(unlinkData->ctx,
687                                    unlinkData->numOfResults, unlinkData->unlinkRes, true);
688         goto error;
689     }
690     unlinkData->resultCallback(unlinkData->ctx, unlinkData->numOfResults, unlinkData->unlinkRes,
691                                false);
692
693 error:
694     DeleteUnlinkData_t(unlinkData);
695     OC_LOG(DEBUG, TAG, "OUT SRPUnlinkDevice2CB");
696     return OC_STACK_DELETE_TRANSACTION;
697
698 }
699
700 /**
701  * Callback handler of unlink first device.
702  *
703  * @param[in] ctx             ctx value passed to callback from calling function.
704  * @param[in] handle          handle to an invocation
705  * @param[in] clientResponse  Response from queries to remote servers.
706  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction and
707  *          OC_STACK_KEEP_TRANSACTION to keep it.
708  */
709 static OCStackApplicationResult SRPUnlinkDevice1CB(void *unlinkCtx, OCDoHandle handle,
710         OCClientResponse *clientResponse)
711 {
712     OC_LOG_V(INFO, TAG, "Inside SRPUnlinkDevice1CB ");
713     VERIFY_NON_NULL(TAG, unlinkCtx, ERROR, OC_STACK_DELETE_TRANSACTION);
714     UnlinkData_t* unlinkData = (UnlinkData_t*)unlinkCtx;
715     (void) handle;
716
717     if (clientResponse)
718     {
719         OC_LOG(DEBUG, TAG, "Valid client response for device 1");
720         registerResultForUnlinkDevices(unlinkData, clientResponse->result, IDX_FIRST_DEVICE_RES);
721
722         if (OC_STACK_RESOURCE_DELETED == clientResponse->result)
723         {
724             OC_LOG(DEBUG, TAG, "Credential of device 1 is revoked");
725
726             // Second revocation request to second device.
727             OCStackResult res = SendDeleteCredentialRequest((void*)unlinkData, &SRPUnlinkDevice2CB,
728                                                     &unlinkData->unlinkDev[0],
729                                                     &unlinkData->unlinkDev[1] /*Dest*/);
730             OC_LOG_V(DEBUG, TAG, "Credential revocation request device 2, result :: %d",res);
731             if (OC_STACK_OK != res)
732             {
733                  OC_LOG(ERROR, TAG, "Error while sending revocation request for device 2");
734                  registerResultForUnlinkDevices(unlinkData, OC_STACK_INVALID_REQUEST_HANDLE,
735                                                 IDX_SECOND_DEVICE_RES);
736                  unlinkData->resultCallback(unlinkData->ctx,
737                                             unlinkData->numOfResults, unlinkData->unlinkRes, true);
738                  goto error;
739             }
740             else
741             {
742                 OC_LOG(DEBUG, TAG, "Request for credential revocation successfully sent");
743                 return OC_STACK_DELETE_TRANSACTION;
744             }
745         }
746         else
747         {
748             OC_LOG(ERROR, TAG, "Unable to delete credential information from device 1");
749
750             unlinkData->resultCallback(unlinkData->ctx, unlinkData->numOfResults,
751                                             unlinkData->unlinkRes, true);
752             goto error;
753         }
754     }
755     else
756     {
757         OC_LOG(DEBUG, TAG, "Invalid response from server");
758         registerResultForUnlinkDevices(unlinkData, OC_STACK_INVALID_REQUEST_HANDLE,
759                                        IDX_FIRST_DEVICE_RES );
760         unlinkData->resultCallback(unlinkData->ctx,
761                                    unlinkData->numOfResults, unlinkData->unlinkRes,
762                                    true);
763         OC_LOG(ERROR, TAG, "SRPUnlinkDevice1CB received Null clientResponse");
764     }
765
766 error:
767     OC_LOG_V(INFO, TAG, "Out SRPUnlinkDevice1CB");
768     DeleteUnlinkData_t(unlinkData);
769     return OC_STACK_DELETE_TRANSACTION;
770 }
771
772 /*
773 * Function to unlink devices.
774 * This function will remove the credential & relationship between the two devices.
775 *
776 * @param[in] ctx Application context would be returned in result callback
777 * @param[in] pTargetDev1 first device information to be unlinked.
778 * @param[in] pTargetDev2 second device information to be unlinked.
779 * @param[in] resultCallback callback provided by API user, callback will be called when
780 *            device unlink is finished.
781  * @return  OC_STACK_OK in case of success and other value otherwise.
782 */
783 OCStackResult SRPUnlinkDevices(void* ctx,
784                                const OCProvisionDev_t* pTargetDev1,
785                                const OCProvisionDev_t* pTargetDev2,
786                                OCProvisionResultCB resultCallback)
787 {
788     OC_LOG(INFO, TAG, "IN SRPUnlinkDevices");
789
790     if (!pTargetDev1 || !pTargetDev2 || !resultCallback)
791     {
792         OC_LOG(INFO, TAG, "SRPUnlinkDevices : NULL parameters");
793         return OC_STACK_INVALID_PARAM;
794     }
795     OC_LOG(INFO, TAG, "Unlinking following devices: ");
796     PMPrintOCProvisionDev(pTargetDev1);
797     PMPrintOCProvisionDev(pTargetDev2);
798
799     // Mark the link status stale
800     OCStackResult res = PDMSetLinkStale(&pTargetDev1->doxm->deviceID, &pTargetDev2->doxm->deviceID);
801     if (OC_STACK_OK != res)
802     {
803         OC_LOG(FATAL, TAG, "unable to update DB. Try again.");
804         return res;
805     }
806
807     UnlinkData_t* unlinkData = (UnlinkData_t*)OICCalloc(1, sizeof(UnlinkData_t));
808     VERIFY_NON_NULL(TAG, unlinkData, ERROR, OC_STACK_NO_MEMORY);
809
810     //Initialize unlink data
811     unlinkData->ctx = ctx;
812     unlinkData->unlinkDev = (OCProvisionDev_t*)OICCalloc(2, sizeof(OCProvisionDev_t));
813     if (NULL == unlinkData->unlinkDev)
814     {
815         OC_LOG(ERROR, TAG, "Memory allocation failed");
816         res = OC_STACK_NO_MEMORY;
817         goto error;
818     }
819
820     unlinkData->unlinkRes = (OCProvisionResult_t*)OICCalloc(3, sizeof(OCProvisionResult_t));
821     if (NULL == unlinkData->unlinkRes)
822     {
823         OC_LOG(ERROR, TAG, "Memory allocation failed");
824         res = OC_STACK_NO_MEMORY;
825         goto error;
826     }
827
828     memcpy(&unlinkData->unlinkDev[0], pTargetDev1, sizeof(OCProvisionDev_t));
829     memcpy(&unlinkData->unlinkDev[1], pTargetDev2, sizeof(OCProvisionDev_t));
830
831     unlinkData->numOfResults = 0;
832     unlinkData->resultCallback = resultCallback;
833
834     res = SendDeleteCredentialRequest((void*)unlinkData, &SRPUnlinkDevice1CB,
835                                        &unlinkData->unlinkDev[1], &unlinkData->unlinkDev[0]);
836     if (OC_STACK_OK != res)
837     {
838         OC_LOG(ERROR, TAG, "SRPUnlinkDevices : SendDeleteCredentialRequest failed");
839         goto error;
840     }
841
842     return res;
843
844 error:
845     OC_LOG(INFO, TAG, "OUT SRPUnlinkDevices");
846     DeleteUnlinkData_t(unlinkData);
847     return res;
848 }
849
850 static void DeleteRemoveData_t(RemoveData_t* pRemoveData)
851 {
852     if (pRemoveData)
853     {
854         OICFree(pRemoveData->revokeTargetDev);
855         OCDeleteDiscoveredDevices(pRemoveData->linkedDevList);
856         OICFree(pRemoveData->removeRes);
857         OICFree(pRemoveData);
858     }
859 }
860
861 static void registerResultForRemoveDevice(RemoveData_t *removeData, OicUuid_t *pLinkedDevId,
862                                           OCStackResult stackresult, bool hasError)
863 {
864     OC_LOG_V(INFO, TAG, "Inside registerResultForRemoveDevice removeData->numOfResults is %d\n",
865                          removeData->numOfResults + 1);
866     if (pLinkedDevId)
867     {
868         memcpy(removeData->removeRes[(removeData->numOfResults)].deviceId.id,
869                &pLinkedDevId->id, sizeof(pLinkedDevId->id));
870     }
871     else
872     {
873         memset(removeData->removeRes[(removeData->numOfResults)].deviceId.id,
874                0, sizeof(pLinkedDevId->id) );
875     }
876     removeData->removeRes[(removeData->numOfResults)].res = stackresult;
877     removeData->hasError = hasError;
878     ++(removeData->numOfResults);
879
880     // If we get suffcient result from linked devices, we have to call user callback and do free
881     if (removeData->sizeOfResArray == removeData->numOfResults)
882     {
883         removeData->resultCallback(removeData->ctx, removeData->numOfResults, removeData->removeRes,
884                                    removeData->hasError);
885         DeleteRemoveData_t(removeData);
886     }
887  }
888
889 /**
890  * Callback handler of unlink first device.
891  *
892  * @param[in] ctx             ctx value passed to callback from calling function.
893  * @param[in] handle          handle to an invocation
894  * @param[in] clientResponse  Response from queries to remote servers.
895  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction
896  *          and  OC_STACK_KEEP_TRANSACTION to keep it.
897  */
898 static OCStackApplicationResult SRPRemoveDeviceCB(void *delDevCtx, OCDoHandle handle,
899         OCClientResponse *clientResponse)
900 {
901     //Update the delete credential into delete device context
902     //Save the deleted status in delDevCtx
903     (void)handle;
904     OC_LOG_V(INFO, TAG, "Inside SRPRemoveDeviceCB.");
905     VERIFY_NON_NULL(TAG, delDevCtx, ERROR, OC_STACK_DELETE_TRANSACTION);
906     OCStackResult res = OC_STACK_ERROR;
907
908     RemoveData_t* removeData = (RemoveData_t*)delDevCtx;
909     if (clientResponse)
910     {
911         // If we can get device's UUID from OCClientResponse, it'd be good to use it in here
912         // but OCIdentity in OCClientResponse is emtpy now.
913         // It seems that we can set identity to CAData_t *cadata in CAPrepareSendData() API
914         // but CA doesn't have deviceID yet.
915         //
916         //TODO: Get OCIdentity from OCClientResponse and use it for 'registerResultForRemoveDevice'
917         //      If we can't complete this task, Provisioning Database has always stale link status
918         //      when Remove device is called.
919
920         if (OC_STACK_RESOURCE_DELETED == clientResponse->result)
921         {
922             res = PDMUnlinkDevices(&removeData->revokeTargetDev->doxm->deviceID,
923                                    NULL /*TODO: Replace NULL to uuid from OCClientResponse*/);
924             if (OC_STACK_OK != res)
925             {
926                 OC_LOG(FATAL, TAG, "PDMSetLinkStale() FAIL: PDB is an obsolete one.");
927                 registerResultForRemoveDevice(removeData,
928                                           NULL /*TODO: Replace NULL to uuid from OCClientResponse*/,
929                                           OC_STACK_INCONSISTENT_DB, true);
930                 return OC_STACK_DELETE_TRANSACTION;
931             }
932             registerResultForRemoveDevice(removeData,
933                                           NULL /*TODO: Replace NULL to uuid from OCClientResponse*/,
934                                           OC_STACK_RESOURCE_DELETED, false);
935         }
936         else
937         {
938             registerResultForRemoveDevice(removeData,
939                                           NULL /*TODO: Replace NULL to uuid from OCClientResponse*/,
940                                           clientResponse->result, true);
941             OC_LOG(ERROR, TAG, "Unexpected result from DELETE credential request!");
942         }
943     }
944     else
945     {
946         registerResultForRemoveDevice(removeData, NULL, OC_STACK_ERROR, true);
947         OC_LOG(ERROR, TAG, "SRPRemoveDevices received Null clientResponse");
948     }
949
950     return OC_STACK_DELETE_TRANSACTION;
951 }
952
953 static OCStackResult GetListofDevToReqDeleteCred(const OCProvisionDev_t* pRevokeTargetDev,
954                                                  OCProvisionDev_t* pOwnedDevList,
955                                                  OCUuidList_t* pLinkedUuidList,
956                                                  OCProvisionDev_t** ppLinkedDevList,
957                                                  size_t *numOfLinkedDev)
958 {
959     // pOwnedDevList could be NULL. It means no alived and owned device now.
960     if (pRevokeTargetDev == NULL || pLinkedUuidList == NULL ||\
961         ppLinkedDevList == NULL || numOfLinkedDev == NULL)
962     {
963         return OC_STACK_INVALID_PARAM;
964     }
965
966     size_t cnt = 0;
967     OCUuidList_t *curUuid = NULL, *tmpUuid = NULL;
968     LL_FOREACH_SAFE(pLinkedUuidList, curUuid, tmpUuid)
969     {
970         // Mark the link status stale.
971         OCStackResult res = PDMSetLinkStale(&curUuid->dev, &pRevokeTargetDev->doxm->deviceID);
972         if (OC_STACK_OK != res)
973         {
974             OC_LOG(FATAL, TAG, "PDMSetLinkStale() FAIL: PDB is an obsolete one.");
975             return OC_STACK_INCONSISTENT_DB;
976         }
977
978         if (pOwnedDevList)
979         {
980             // If this linked device is alive (power-on), add the deivce to the list.
981             OCProvisionDev_t *curDev = NULL, *tmpDev = NULL;
982             LL_FOREACH_SAFE(pOwnedDevList, curDev, tmpDev)
983             {
984                 if (memcmp(curDev->doxm->deviceID.id, curUuid->dev.id, sizeof(curUuid->dev.id)) == 0)
985                 {
986                     OCProvisionDev_t* targetDev = PMCloneOCProvisionDev(curDev);
987                     if (NULL == targetDev)
988                     {
989                         OC_LOG(ERROR, TAG, "SRPRemoveDevice : Cloning OCProvisionDev_t Failed.");
990                         return OC_STACK_NO_MEMORY;
991                     }
992
993                     LL_PREPEND(*ppLinkedDevList, targetDev);
994                     cnt++;
995                     break;
996                 }
997             }
998         }
999     }
1000     *numOfLinkedDev = cnt;
1001     return OC_STACK_OK;
1002 }
1003
1004 /*
1005 * Function to device revocation
1006 * This function will remove credential of target device from all devices in subnet.
1007 *
1008 * @param[in] ctx Application context would be returned in result callback
1009 * @param[in] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds)
1010 * @param[in] pTargetDev Device information to be revoked.
1011 * @param[in] resultCallback callback provided by API user, callback will be called when
1012 *            credential revocation is finished.
1013 * @return  OC_STACK_OK in case of success and other value otherwise.
1014 *          If OC_STACK_OK is returned, the caller of this API should wait for callback.
1015 *          OC_STACK_CONTINUE means operation is success but no request is need to be initiated.
1016 */
1017 OCStackResult SRPRemoveDevice(void* ctx, unsigned short waitTimeForOwnedDeviceDiscovery,
1018                              const OCProvisionDev_t* pTargetDev, OCProvisionResultCB resultCallback)
1019 {
1020     OC_LOG(INFO, TAG, "IN SRPRemoveDevice");
1021
1022     if (!pTargetDev || !resultCallback || 0 == waitTimeForOwnedDeviceDiscovery)
1023     {
1024         OC_LOG(INFO, TAG, "SRPRemoveDevice : NULL parameters");
1025         return OC_STACK_INVALID_PARAM;
1026     }
1027
1028     // Declare variables in here to handle error cases with goto statement.
1029     OCProvisionDev_t* pOwnedDevList = NULL;
1030     OCProvisionDev_t* pLinkedDevList = NULL;
1031     RemoveData_t* removeData = NULL;
1032
1033     //1. Find all devices that has a credential of the revoked device
1034     OCUuidList_t* pLinkedUuidList = NULL;
1035     size_t numOfDevices = 0;
1036     OCStackResult res = OC_STACK_ERROR;
1037     res = PDMGetLinkedDevices(&pTargetDev->doxm->deviceID, &pLinkedUuidList, &numOfDevices);
1038     if (OC_STACK_OK != res)
1039     {
1040         OC_LOG(ERROR, TAG, "SRPRemoveDevice : Failed to get linked devices information");
1041         return res;
1042     }
1043     // if there is no related device, we can skip further process.
1044     if (0 == numOfDevices)
1045     {
1046         OC_LOG(DEBUG, TAG, "SRPRemoveDevice : No linked device found.");
1047         res = OC_STACK_CONTINUE;
1048         goto error;
1049     }
1050
1051     //2. Find owned device from the network
1052     res = PMDeviceDiscovery(waitTimeForOwnedDeviceDiscovery, true, &pOwnedDevList);
1053     if (OC_STACK_OK != res)
1054     {
1055         OC_LOG(ERROR, TAG, "SRPRemoveDevice : Failed to PMDeviceDiscovery");
1056         goto error;
1057     }
1058
1059     //3. Make a list of devices to send DELETE credential request
1060     //   by comparing owned devices from provisioning database with mutlicast discovery result.
1061     size_t numOfLinkedDev = 0;
1062     res = GetListofDevToReqDeleteCred(pTargetDev, pOwnedDevList, pLinkedUuidList,
1063                                       &pLinkedDevList, &numOfLinkedDev);
1064     if (OC_STACK_OK != res)
1065     {
1066         OC_LOG(ERROR, TAG, "SRPRemoveDevice : GetListofDevToReqDeleteCred() failed");
1067         goto error;
1068     }
1069     if (0 == numOfLinkedDev) // This case means, there is linked device but it's not alive now.
1070     {                       // So we don't have to send request message.
1071         OC_LOG(DEBUG, TAG, "SRPRemoveDevice : No alived & linked device found.");
1072         res = OC_STACK_CONTINUE;
1073         goto error;
1074     }
1075
1076     // 4. Prepare RemoveData Context data.
1077     removeData = (RemoveData_t*)OICCalloc(1, sizeof(RemoveData_t));
1078     if (!removeData)
1079     {
1080         OC_LOG(ERROR, TAG, "SRPRemoveDevices : Failed to memory allocation");
1081         res = OC_STACK_NO_MEMORY;
1082         goto error;
1083     }
1084
1085     removeData->revokeTargetDev = PMCloneOCProvisionDev(pTargetDev);
1086     if (!removeData->revokeTargetDev)
1087     {
1088         OC_LOG(ERROR, TAG, "SRPRemoveDevices : PMCloneOCProvisionDev Failed");
1089         res = OC_STACK_NO_MEMORY;
1090         goto error;
1091     }
1092
1093     removeData->removeRes =
1094         (OCProvisionResult_t*)OICCalloc(numOfLinkedDev, sizeof(OCProvisionResult_t));
1095     if (!removeData->removeRes)
1096     {
1097         OC_LOG(ERROR, TAG, "SRPRemoveDevices : Failed to memory allocation");
1098         res = OC_STACK_NO_MEMORY;
1099         goto error;
1100     }
1101
1102     removeData->ctx = ctx;
1103     removeData->linkedDevList = pLinkedDevList;
1104     removeData->resultCallback = resultCallback;
1105     removeData->numOfResults = 0;
1106     removeData->sizeOfResArray = numOfLinkedDev;
1107     removeData->hasError = false;
1108
1109     // 5. Send DELETE credential request to linked devices.
1110     OCProvisionDev_t *curDev = NULL, *tmpDev = NULL;
1111     OCStackResult totalRes = OC_STACK_ERROR;  /* variable for checking request is sent or not */
1112     LL_FOREACH_SAFE(pLinkedDevList, curDev, tmpDev)
1113     {
1114         res = SendDeleteCredentialRequest((void*)removeData, &SRPRemoveDeviceCB,
1115                                            removeData->revokeTargetDev, curDev);
1116         if (OC_STACK_OK != res)
1117         {
1118             OC_LOG_V(ERROR, TAG, "SRPRemoveDevice : Fail to send the DELETE credential request to\
1119                      %s:%u", curDev->endpoint.addr, curDev->endpoint.port);
1120         }
1121         else
1122         {
1123             totalRes = OC_STACK_OK; // This means at least one request is successfully sent.
1124         }
1125     }
1126
1127     PDMDestoryOicUuidLinkList(pLinkedUuidList); //TODO: Modify API name to have unified convention.
1128     PMDeleteDeviceList(pOwnedDevList);
1129     OC_LOG(INFO, TAG, "OUT SRPRemoveDevice");
1130
1131     return totalRes; // Caller of this API should wait callback if totalRes == OC_STACK_OK.
1132
1133 error:
1134     PDMDestoryOicUuidLinkList(pLinkedUuidList);
1135     PMDeleteDeviceList(pOwnedDevList);
1136     PMDeleteDeviceList(pLinkedDevList);
1137     if (removeData)
1138     {
1139         OICFree(removeData->revokeTargetDev);
1140         OICFree(removeData->removeRes);
1141         OICFree(removeData);
1142     }
1143     OC_LOG(INFO, TAG, "OUT ERROR case SRPRemoveDevice");
1144     return res;
1145 }