Add PIN based OxM for security provisioning
[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
40 #define SRP_MAX_URI_LENGTH 512
41 #define TAG "SRPAPI"
42
43 /**
44  * Macro to verify argument is not equal to NULL.
45  * eg: VERIFY_NON_NULL(TAG, ptrData, ERROR,OC_STACK_ERROR);
46  */
47 #define VERIFY_NON_NULL(tag, arg, logLevel, retValue) { if (NULL == (arg)) \
48             { OC_LOG((logLevel), tag, (#arg " is NULL")); return retValue; } }
49
50 /**
51  * Macro to verify success of operation.
52  * eg: VERIFY_SUCCESS(TAG, OC_STACK_OK == foo(), ERROR, OC_STACK_ERROR);
53  */
54 #define VERIFY_SUCCESS(tag, op, logLevel, retValue) { if (!(op)) \
55             {OC_LOG((logLevel), tag, (#op " failed!!")); return retValue;} }
56
57 /**
58  * Structure to carry credential data to callback.
59  */
60 typedef struct CredentialData CredentialData_t;
61 struct CredentialData
62 {
63     void *ctx;                                  /**< Pointer to user context.**/
64     const OCProvisionDev_t *deviceInfo1;        /**< Pointer to OCProvisionDev_t.**/
65     const OCProvisionDev_t *deviceInfo2;        /**< Pointer to OCProvisionDev_t.**/
66     OicSecCred_t *credInfo;                     /**< Pointer to OicSecCred_t.**/
67     OicSecCred_t *credInfoFirst;                /**< Pointer to OicSecCred_t.**/
68     OCProvisionResultCB resultCallback;         /**< Pointer to result callback.**/
69     OCProvisionResult_t *resArr;                /**< Result array.**/
70     int numOfResults;                           /**< Number of results in result array.**/
71 };
72
73 /**
74  * Structure to carry ACL provision API data to callback.
75  */
76 typedef struct ACLData ACLData_t;
77 struct ACLData
78 {
79     void *ctx;                                  /**< Pointer to user context.**/
80     const OCProvisionDev_t *deviceInfo;         /**< Pointer to PMDevInfo_t.**/
81     OCProvisionResultCB resultCallback;         /**< Pointer to result callback.**/
82     OCProvisionResult_t *resArr;                /**< Result array.**/
83     int numOfResults;                           /**< Number of results in result array.**/
84 };
85
86 /**
87  * Function prototype
88  */
89 static OCStackResult provisionCredentials(const OicSecCred_t *cred,
90         const OCProvisionDev_t *deviceInfo, CredentialData_t *credData,
91         OCClientResponseHandler responseHandler);
92
93
94 /**
95  * Internal function to update result in result array.
96  */
97 static void registerResultForCredProvisioning(CredentialData_t *credData,
98                                               OCStackResult stackresult, int cause)
99 {
100
101    OC_LOG_V(INFO,TAG,"value of credData->numOfResults is %d",credData->numOfResults);
102    if(1 == cause)
103    {
104        memcpy(credData->resArr[(credData->numOfResults)].deviceId.id,
105               credData->deviceInfo1->doxm->deviceID.id,UUID_LENGTH);
106    }
107    else
108    {
109        memcpy(credData->resArr[(credData->numOfResults)].deviceId.id,
110               credData->deviceInfo2->doxm->deviceID.id,UUID_LENGTH);
111    }
112    credData->resArr[(credData->numOfResults)].res = stackresult;
113    ++(credData->numOfResults);
114 }
115
116 /**
117  * Callback handler for handling callback of provisioning device 2.
118  *
119  * @param[in] ctx             ctx value passed to callback from calling function.
120  * @param[in] UNUSED          handle to an invocation
121  * @param[in] clientResponse  Response from queries to remote servers.
122  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction
123  *          and  OC_STACK_KEEP_TRANSACTION to keep it.
124  */
125 static OCStackApplicationResult provisionCredentialCB2(void *ctx, OCDoHandle UNUSED,
126         OCClientResponse *clientResponse)
127 {
128     VERIFY_NON_NULL(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
129     CredentialData_t *credData = (CredentialData_t *) ctx;
130
131     OCProvisionResultCB resultCallback = credData->resultCallback;
132     OC_LOG(INFO, TAG, "provisionCredentialCB2 called");
133     if (clientResponse)
134     {
135         if(OC_STACK_RESOURCE_CREATED == clientResponse->result)
136         {
137             registerResultForCredProvisioning(credData, OC_STACK_RESOURCE_CREATED, 2);
138             ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
139                                                     credData->resArr,
140                                                     false);
141              OICFree(credData->resArr);
142              OICFree(credData);
143              return OC_STACK_DELETE_TRANSACTION;
144         }
145
146     }
147     OC_LOG(INFO, TAG, "provisionCredentialCB2 received Null clientResponse");
148     registerResultForCredProvisioning(credData, OC_STACK_ERROR, 2);
149     ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
150                                             credData->resArr,
151                                             true);
152     OICFree(credData->resArr);
153     OICFree(credData);
154     return OC_STACK_DELETE_TRANSACTION;
155 }
156
157 /**
158  * Callback handler for handling callback of provisioning device 1.
159  *
160  * @param[in] ctx             ctx value passed to callback from calling function.
161  * @param[in] UNUSED          handle to an invocation
162  * @param[in] clientResponse  Response from queries to remote servers.
163  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction
164  *          and  OC_STACK_KEEP_TRANSACTION to keep it.
165  */
166 static OCStackApplicationResult provisionCredentialCB1(void *ctx, OCDoHandle UNUSED,
167         OCClientResponse *clientResponse)
168 {
169     VERIFY_NON_NULL(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
170     CredentialData_t* credData = (CredentialData_t*) ctx;
171     OICFree(credData->credInfoFirst);
172     const OCProvisionDev_t *deviceInfo = credData->deviceInfo2;
173     OicSecCred_t *credInfo = credData->credInfo;
174     const OCProvisionResultCB resultCallback = credData->resultCallback;
175     if (clientResponse)
176     {
177         if (OC_STACK_RESOURCE_CREATED == clientResponse->result)
178         {
179             // send credentials to second device
180             registerResultForCredProvisioning(credData, OC_STACK_RESOURCE_CREATED,1);
181             OCStackResult res = provisionCredentials(credInfo, deviceInfo, credData,
182                     provisionCredentialCB2);
183             DeleteCredList(credInfo);
184             if (OC_STACK_OK != res)
185             {
186                 registerResultForCredProvisioning(credData, res,2);
187                 ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
188                                                         credData->resArr,
189                                                         true);
190                 OICFree(credData->resArr);
191                 OICFree(credData);
192                 credData = NULL;
193             }
194         }
195         else
196         {
197             registerResultForCredProvisioning(credData, OC_STACK_ERROR,1);
198             ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
199                                                     credData->resArr,
200                                                     true);
201             OICFree(credData->resArr);
202             OICFree(credData);
203             credData = NULL;
204         }
205     }
206     else
207     {
208         OC_LOG(INFO, TAG, "provisionCredentialCB received Null clientResponse for first device");
209         registerResultForCredProvisioning(credData, OC_STACK_ERROR,1);
210        ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
211                                                      credData->resArr,
212                                                      true);
213         DeleteCredList(credInfo);
214         OICFree(credData->resArr);
215         OICFree(credData);
216         credData = NULL;
217     }
218     return OC_STACK_DELETE_TRANSACTION;
219 }
220
221
222
223 /**
224  * Internal function for handling credential generation and sending credential to resource server.
225  *
226  * @param[in] cred Instance of cred resource.
227  * @param[in] deviceInfo information about device to which credential is to be provisioned.
228  * @param[in] responseHandler callbak called by OC stack when request API receives response.
229  * @return  OC_STACK_OK in case of success and other value otherwise.
230  */
231 static OCStackResult provisionCredentials(const OicSecCred_t *cred,
232         const OCProvisionDev_t *deviceInfo, CredentialData_t *credData,
233         OCClientResponseHandler responseHandler)
234 {
235     OCSecurityPayload* secPayload = (OCSecurityPayload*)OICCalloc(1, sizeof(OCSecurityPayload));
236     if(!secPayload)
237     {
238         OC_LOG(ERROR, TAG, "Failed to memory allocation");
239         return OC_STACK_NO_MEMORY;
240     }
241     secPayload->base.type = PAYLOAD_TYPE_SECURITY;
242     secPayload->securityData = BinToCredJSON(cred);
243     if(NULL == secPayload->securityData)
244     {
245         OICFree(secPayload);
246         OC_LOG(ERROR, TAG, "Failed to BinToCredJSON");
247         return OC_STACK_NO_MEMORY;
248     }
249
250     OC_LOG_V(INFO, TAG, "Credential for provisioning : %s",secPayload->securityData);
251     char uri[SRP_MAX_URI_LENGTH] = { 0 };
252
253     size_t uriLen = sizeof(uri);
254     snprintf(uri, uriLen - 1, COAPS_QUERY, deviceInfo->endpoint.addr, deviceInfo->securePort,
255             OIC_RSRC_CRED_URI);
256
257     uri[uriLen - 1] = '\0';
258     OC_LOG_V(INFO, TAG, "URI for Credential provisioning : %s",uri);
259     OCCallbackData cbData = {.context=NULL, .cb=NULL, .cd=NULL};
260     cbData.cb = responseHandler;
261     cbData.context = (void *) credData;
262     cbData.cd = NULL;
263
264     OCDoHandle handle = NULL;
265     OCMethod method = OC_REST_POST;
266     // TODO replace CT_ADAPTER_IP with value from discovery
267     OCStackResult ret = OCDoResource(&handle, method, uri, 0, (OCPayload*)secPayload,
268             CT_ADAPTER_IP, OC_HIGH_QOS, &cbData, NULL, 0);
269     OC_LOG_V(INFO, TAG, "OCDoResource::Credential provisioning returned : %d",ret);
270     if (ret != OC_STACK_OK)
271     {
272         OC_LOG(ERROR, TAG, "OCStack resource error");
273         return ret;
274     }
275     return OC_STACK_OK;
276 }
277
278 OCStackResult SRPProvisionCredentials(void *ctx, OicSecCredType_t type, size_t keySize,
279                                       const OCProvisionDev_t *pDev1,
280                                       const OCProvisionDev_t *pDev2,
281                                       OCProvisionResultCB resultCallback)
282 {
283     VERIFY_NON_NULL(TAG, pDev1, ERROR,  OC_STACK_INVALID_PARAM);
284     VERIFY_NON_NULL(TAG, pDev2, ERROR,  OC_STACK_INVALID_PARAM);
285     VERIFY_NON_NULL(TAG, resultCallback, ERROR,  OC_STACK_INVALID_CALLBACK);
286
287     if (!(keySize == OWNER_PSK_LENGTH_128 || keySize == OWNER_PSK_LENGTH_256))
288     {
289         OC_LOG(INFO, TAG, "Invalid key size");
290         return OC_STACK_INVALID_PARAM;
291     }
292
293     OC_LOG(INFO, TAG, "In SRPProvisionCredentials");
294
295     OicUuid_t provTooldeviceID =   {{0,}};
296     if (OC_STACK_OK != GetDoxmDeviceID(&provTooldeviceID))
297     {
298         OC_LOG(ERROR, TAG, "Error while retrieving provisioning tool's device ID");
299         return OC_STACK_ERROR;
300     }
301     OC_LOG(INFO, TAG, "retrieved deviceid");
302     switch (type)
303     {
304         case SYMMETRIC_PAIR_WISE_KEY:
305         {
306             const OCProvisionDev_t *firstDevice = pDev1;
307             const OCProvisionDev_t *secondDevice = pDev2;
308
309             OicSecCred_t *firstCred = NULL;
310             OicSecCred_t *secondCred = NULL;
311             OCStackResult res = PMGeneratePairWiseCredentials(type, keySize, &provTooldeviceID,
312                     &firstDevice->doxm->deviceID, &secondDevice->doxm->deviceID,
313                     &firstCred, &secondCred);
314             VERIFY_SUCCESS(TAG, (res==OC_STACK_OK), ERROR, OC_STACK_ERROR);
315             OC_LOG(INFO, TAG, "Credentials generated successfully");
316             CredentialData_t *credData = (CredentialData_t *) OICMalloc(sizeof(CredentialData_t));
317             if (NULL == credData)
318             {
319                 OC_LOG(ERROR, TAG, "Memory allocation problem");
320                 return OC_STACK_NO_MEMORY;
321             }
322             memset(credData, 0x00, sizeof(CredentialData_t));
323             credData->deviceInfo1 = firstDevice;
324             credData->deviceInfo2 = secondDevice;
325             credData->credInfo = secondCred;
326             credData->ctx = ctx;
327             credData->credInfoFirst = firstCred;
328             credData->numOfResults = 0;
329             credData->resultCallback = resultCallback;
330             // first call to provision creds to device1.
331             // second call to provision creds to device2.
332             int noOfRiCalls = 2;
333             credData->resArr =
334                 (OCProvisionResult_t*)OICMalloc(sizeof(OCProvisionResult_t) * noOfRiCalls);
335             if (NULL == credData->resArr)
336             {
337                 OC_LOG(ERROR, TAG, "Memory allocation problem");
338                 return OC_STACK_NO_MEMORY;
339             }
340             memset(credData->resArr, 0x00, sizeof(sizeof(OCProvisionResult_t)*noOfRiCalls));
341             res = provisionCredentials(firstCred, firstDevice, credData, &provisionCredentialCB1);
342             if (OC_STACK_OK != res)
343             {
344                 DeleteCredList(firstCred);
345                 DeleteCredList(secondCred);
346                 OICFree(credData->resArr);
347                 OICFree(credData);
348             }
349             OC_LOG_V(INFO, TAG, "provisionCredentials returned: %d",res);
350             VERIFY_SUCCESS(TAG, (res==OC_STACK_OK), ERROR, OC_STACK_ERROR);
351             return res;
352         }
353         default:
354         {
355             OC_LOG(ERROR, TAG, "Invalid option.");
356             return OC_STACK_INVALID_PARAM;
357         }
358     }
359     return OC_STACK_ERROR;
360 }
361
362 /**
363  * Internal Function to store results in result array during ACL provisioning.
364  */
365 static void registerResultForACLProvisioning(ACLData_t *aclData,
366                                              OCStackResult stackresult)
367 {
368    OC_LOG_V(INFO, TAG, "Inside registerResultForACLProvisioning aclData->numOfResults is %d\n",
369                        aclData->numOfResults);
370    memcpy(aclData->resArr[(aclData->numOfResults)].deviceId.id,
371           aclData->deviceInfo->doxm->deviceID.id, UUID_LENGTH);
372    aclData->resArr[(aclData->numOfResults)].res = stackresult;
373    ++(aclData->numOfResults);
374 }
375
376 /**
377  * Callback handler of SRPProvisionACL.
378  *
379  * @param[in] ctx             ctx value passed to callback from calling function.
380  * @param[in] UNUSED          handle to an invocation
381  * @param[in] clientResponse  Response from queries to remote servers.
382  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction
383  *          and  OC_STACK_KEEP_TRANSACTION to keep it.
384  */
385 static OCStackApplicationResult SRPProvisionACLCB(void *ctx, OCDoHandle UNUSED,
386         OCClientResponse *clientResponse)
387 {
388     OC_LOG_V(INFO, TAG, "Inside SRPProvisionACLCB.");
389     VERIFY_NON_NULL(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
390     ACLData_t *aclData = (ACLData_t*)ctx;
391     OCProvisionResultCB resultCallback = aclData->resultCallback;
392
393     if (clientResponse)
394     {
395         if(OC_STACK_RESOURCE_CREATED == clientResponse->result)
396         {
397             registerResultForACLProvisioning(aclData, OC_STACK_RESOURCE_CREATED);
398             ((OCProvisionResultCB)(resultCallback))(aclData->ctx, aclData->numOfResults,
399                                                     aclData->resArr,
400                                                     false);
401              OICFree(aclData->resArr);
402              OICFree(aclData);
403              return OC_STACK_DELETE_TRANSACTION;
404         }
405     }
406     registerResultForACLProvisioning(aclData, OC_STACK_ERROR);
407     ((OCProvisionResultCB)(resultCallback))(aclData->ctx, aclData->numOfResults,
408                                             aclData->resArr,
409                                             true);
410     OC_LOG_V(ERROR, TAG, "SRPProvisionACLCB received Null clientResponse");
411     OICFree(aclData->resArr);
412     OICFree(aclData);
413     return OC_STACK_DELETE_TRANSACTION;
414 }
415
416 OCStackResult SRPProvisionACL(void *ctx, const OCProvisionDev_t *selectedDeviceInfo,
417         OicSecAcl_t *acl, OCProvisionResultCB resultCallback)
418 {
419     VERIFY_NON_NULL(TAG, selectedDeviceInfo, ERROR,  OC_STACK_INVALID_PARAM);
420     VERIFY_NON_NULL(TAG, acl, ERROR,  OC_STACK_INVALID_PARAM);
421     VERIFY_NON_NULL(TAG, resultCallback, ERROR,  OC_STACK_INVALID_CALLBACK);
422
423     OCSecurityPayload* secPayload = (OCSecurityPayload*)OICCalloc(1, sizeof(OCSecurityPayload));
424     if(!secPayload)
425     {
426         OC_LOG(ERROR, TAG, "Failed to memory allocation");
427         return OC_STACK_NO_MEMORY;
428     }
429     secPayload->base.type = PAYLOAD_TYPE_SECURITY;
430     secPayload->securityData = BinToAclJSON(acl);
431     if(NULL == secPayload->securityData)
432     {
433         OICFree(secPayload);
434         OC_LOG(ERROR, TAG, "Failed to BinToAclJSON");
435         return OC_STACK_NO_MEMORY;
436     }
437     OC_LOG_V(INFO, TAG, "ACL : %s", secPayload->securityData);
438
439     char uri[SRP_MAX_URI_LENGTH] = {0};
440     size_t uriLen = sizeof(uri);
441
442     snprintf(uri, uriLen - 1, COAPS_QUERY, selectedDeviceInfo->endpoint.addr,
443             selectedDeviceInfo->securePort, OIC_RSRC_ACL_URI);
444     uri[uriLen - 1] = '\0';
445
446     OC_LOG_V(INFO, TAG, "URI : %s", uri);
447     OCCallbackData cbData =  {.context=NULL, .cb=NULL, .cd=NULL};
448     cbData.cb = &SRPProvisionACLCB;
449     ACLData_t *aclData = (ACLData_t *) OICMalloc(sizeof(ACLData_t));
450     if (aclData == NULL)
451     {
452         OICFree(secPayload);
453         OC_LOG(ERROR, TAG, "Unable to allocate memory");
454         return OC_STACK_NO_MEMORY;
455     }
456     memset(aclData, 0x00, sizeof(ACLData_t));
457     aclData->deviceInfo = selectedDeviceInfo;
458     aclData->resultCallback = resultCallback;
459     aclData->numOfResults=0;
460     aclData->ctx = ctx;
461     // call to provision ACL to device1.
462     int noOfRiCalls = 1;
463     aclData->resArr = (OCProvisionResult_t*)OICMalloc(sizeof(OCProvisionResult_t)*noOfRiCalls);
464     if (aclData->resArr == NULL)
465     {
466         OICFree(secPayload);
467         OC_LOG(ERROR, TAG, "Unable to allocate memory");
468         return OC_STACK_NO_MEMORY;
469     }
470     memset(aclData->resArr, 0x00, sizeof(sizeof(OCProvisionResult_t)*noOfRiCalls));
471     cbData.context = (void *)aclData;
472     cbData.cd = NULL;
473     OCMethod method = OC_REST_POST;
474     OCDoHandle handle = NULL;
475     OC_LOG(DEBUG, TAG, "Sending ACL info to resource server");
476     // TODO replace CT_ADAPTER_IP with value from discovery
477
478     OCStackResult ret = OCDoResource(&handle, method, uri,
479             &selectedDeviceInfo->endpoint, (OCPayload*)secPayload,
480             CT_ADAPTER_IP, OC_HIGH_QOS, &cbData, NULL, 0);
481     if (ret != OC_STACK_OK)
482     {
483         OICFree(aclData->resArr);
484         OICFree(aclData);
485     }
486     VERIFY_SUCCESS(TAG, (OC_STACK_OK == ret), ERROR, OC_STACK_ERROR);
487     return OC_STACK_OK;
488 }