Merge branch 'notification-service'
[platform/upstream/iotivity.git] / service / easy-setup / mediator / richsdk / src / EnrolleeSecurity.cpp
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
21 #include "base64.h"
22
23 #include "EnrolleeSecurity.h"
24 #include "oxmjustworks.h"
25 #include "oxmrandompin.h"
26 #include "EnrolleeResource.h"
27 #include "logger.h"
28 #include "ESException.h"
29 #include "oic_malloc.h"
30 #include "oic_string.h"
31 #include "utlist.h"
32
33 namespace OIC
34 {
35     namespace Service
36     {
37         #define MAX_PERMISSION_LENGTH (5)
38         #define CREATE (1)
39         #define READ (2)
40         #define UPDATE (4)
41         #define DELETE (8)
42         #define NOTIFY (16)
43         #define DASH '-'
44
45         //TODO : Currently discovery timeout for owned and unowned devices is fixed as 5
46         // The value should be accepted from the application as a parameter during ocplatform
47         // config call
48         #define ES_SEC_DISCOVERY_TIMEOUT 5
49
50         EnrolleeSecurity::EnrolleeSecurity(
51         std::shared_ptr< OC::OCResource > resource,
52         std::string secDbPath)
53         {
54             (void) secDbPath;
55             m_ocResource = resource;
56         }
57
58         void EnrolleeSecurity::registerCallbackHandler(SecurityProvStatusCb securityProvStatusCb,
59                 SecurityPinCb securityPinCb, SecProvisioningDbPathCb secProvisioningDbPathCb)
60         {
61             m_securityProvStatusCb = securityProvStatusCb;
62             m_securityPinCb = securityPinCb;
63             m_secProvisioningDbPathCb = secProvisioningDbPathCb;
64         }
65
66         std::shared_ptr< OC::OCSecureResource > EnrolleeSecurity::getEnrollee(DeviceList_t &list)
67         {
68             for (unsigned int i = 0; i < list.size(); i++)
69             {
70                 if(m_ocResource->sid() == list[i]->getDeviceID().c_str())
71                 {
72                     OIC_LOG_V(DEBUG, ENROLEE_SECURITY_TAG, "Device %d ID %s ", i + 1,
73                             list[i]->getDeviceID().c_str());
74                     OIC_LOG_V(DEBUG, ENROLEE_SECURITY_TAG, "From IP :%s", list[i]->getDevAddr().c_str());
75                     return list[i];
76                 }
77             }
78             OIC_LOG(ERROR, ENROLEE_SECURITY_TAG,"Error!!! DeviceList_t is NULL");
79             return NULL;
80         }
81
82         void EnrolleeSecurity::convertUUIDToString(OicUuid_t uuid, std::string& uuidString)
83         {
84             char base64Buff[B64ENCODE_OUT_SAFESIZE(sizeof(((OicUuid_t*) 0)->id)) + 1] =
85             { 0, };
86             uint32_t outLen = 0;
87             B64Result b64Ret = B64_OK;
88             std::ostringstream deviceId("");
89
90             b64Ret = b64Encode(uuid.id, sizeof(uuid.id),
91                     base64Buff, sizeof(base64Buff), &outLen);
92
93             if (B64_OK == b64Ret)
94             {
95                 deviceId << base64Buff;
96             }
97             uuidString =  deviceId.str();
98         }
99
100         void EnrolleeSecurity::convertStringToUUID(OicUuid_t& uuid, std::string uuidString)
101         {
102             size_t outBufSize = B64DECODE_OUT_SAFESIZE((uuidString.length() + 1));
103             uint8_t* outKey = (uint8_t*)OICCalloc(1, outBufSize);
104             uint32_t outKeySize = 0;
105             if(NULL == outKey)
106             {
107                 OIC_LOG (ERROR, ENROLEE_SECURITY_TAG, "Failed to memoray allocation.");
108                 throw ESBadRequestException ("Failed to memoray allocation.");
109             }
110
111             if(B64_OK == b64Decode((char*)uuidString.c_str(),
112                                     uuidString.length(),
113                                     outKey,
114                                     outBufSize,
115                                     &outKeySize))
116             {
117                 memcpy(uuid.id, outKey, outKeySize);
118             }
119             else
120             {
121                 OIC_LOG (ERROR, ENROLEE_SECURITY_TAG, "Failed to base64 decoding.");
122                 throw ESBadRequestException ("Failed to base64 decoding.");
123             }
124
125             OICFree(outKey);
126         }
127
128         void EnrolleeSecurity::ownershipTransferCb(OC::PMResultList_t *result, int hasError)
129         {
130             if (hasError)
131             {
132                 OIC_LOG(ERROR, ENROLEE_SECURITY_TAG,"Error!!! in OwnershipTransfer");
133
134                 std::string uuid;
135                 convertUUIDToString(result->at(0).deviceId, uuid);
136                 std::shared_ptr< SecProvisioningStatus > securityProvisioningStatus =
137                         std::make_shared< SecProvisioningStatus >(uuid, ES_ERROR);
138                 m_securityProvStatusCb(securityProvisioningStatus);
139                 return;
140             }
141             else
142             {
143                 OIC_LOG(DEBUG, ENROLEE_SECURITY_TAG, "ownershipTransferCb : Received provisioning results: ");
144                 for (unsigned int i = 0; i < result->size(); i++)
145                 {
146                     OIC_LOG_V(DEBUG, ENROLEE_SECURITY_TAG, "Result is = %d for device",result->at(i).res);
147                     std::string uuid;
148                     convertUUIDToString(result->at(0).deviceId, uuid);
149
150                     OIC_LOG_V(DEBUG, ENROLEE_SECURITY_TAG, "UUID : %s",uuid.c_str());
151                     std::shared_ptr< SecProvisioningStatus > securityProvisioningStatus =
152                             std::make_shared< SecProvisioningStatus >(uuid, ES_OK);
153                     m_securityProvStatusCb(securityProvisioningStatus);
154                     return;
155                 }
156
157                 delete result;
158             }
159         }
160
161         void EnrolleeSecurity::performOwnershipTransfer()
162         {
163             OC::DeviceList_t pUnownedDevList, pOwnedDevList;
164
165             pOwnedDevList.clear();
166             pUnownedDevList.clear();
167
168             OCStackResult result = OC_STACK_ERROR;
169
170             result = OCSecure::discoverOwnedDevices(ES_SEC_DISCOVERY_TIMEOUT,
171                     pOwnedDevList);
172             if (result != OC_STACK_OK)
173             {
174                 OIC_LOG(ERROR, ENROLEE_SECURITY_TAG, "Owned Discovery failed.");
175                 //Throw exception
176                 throw ESPlatformException(result);
177             }
178             else if (pOwnedDevList.size())
179             {
180                 OIC_LOG_V(DEBUG, ENROLEE_SECURITY_TAG, "Found owned devices. Count =%d",
181                         pOwnedDevList.size());
182                 std::shared_ptr< OC::OCSecureResource > ownedDevice = getEnrollee(pOwnedDevList);
183
184                 if (ownedDevice)
185                 {
186                     std::shared_ptr< SecProvisioningStatus > securityProvisioningStatus =
187                             std::make_shared< SecProvisioningStatus >(ownedDevice->getDeviceID(), ES_OK);
188                     m_securityProvStatusCb(securityProvisioningStatus);
189                     return;
190                 }
191             }
192
193             result = OCSecure::discoverUnownedDevices(ES_SEC_DISCOVERY_TIMEOUT, pUnownedDevList);
194             if (result != OC_STACK_OK)
195             {
196                 OIC_LOG(ERROR, ENROLEE_SECURITY_TAG, "UnOwned Discovery failed.");
197                 //Throw exception
198                 throw ESPlatformException(result);
199             }
200             else if (pUnownedDevList.size())
201             {
202                 OIC_LOG_V(DEBUG, ENROLEE_SECURITY_TAG, "Found Unowned devices. Count =%d",
203                         pUnownedDevList.size());
204
205                 m_unownedDevice = getEnrollee(pUnownedDevList);
206                 if (m_unownedDevice)
207                 {
208                     OTMCallbackData_t justWorksCBData;
209                     justWorksCBData.loadSecretCB = LoadSecretJustWorksCallback;
210                     justWorksCBData.createSecureSessionCB = CreateSecureSessionJustWorksCallback;
211                     justWorksCBData.createSelectOxmPayloadCB = CreateJustWorksSelectOxmPayload;
212                     justWorksCBData.createOwnerTransferPayloadCB =
213                             CreateJustWorksOwnerTransferPayload;
214                     OCSecure::setOwnerTransferCallbackData(OIC_JUST_WORKS, &justWorksCBData, NULL);
215
216                     OIC_LOG_V(DEBUG, ENROLEE_SECURITY_TAG, "Transfering ownership for : %s ",
217                             m_unownedDevice->getDeviceID().c_str());
218
219                     OC::ResultCallBack ownershipTransferCb = std::bind(
220                             &EnrolleeSecurity::ownershipTransferCb, this, std::placeholders::_1,
221                             std::placeholders::_2);
222
223                     result = m_unownedDevice->doOwnershipTransfer(ownershipTransferCb);
224                     if (result != OC_STACK_OK)
225                     {
226                         OIC_LOG(ERROR, ENROLEE_SECURITY_TAG, "OwnershipTransferCallback is failed");
227                         throw ESPlatformException(result);
228                     }
229                 }
230                 else
231                 {
232                     OIC_LOG(ERROR, ENROLEE_SECURITY_TAG, "No matched unowned devices found.");
233                     throw ESException("No matched unowned devices found.");
234                 }
235             }
236             else
237             {
238                 OIC_LOG(ERROR, ENROLEE_SECURITY_TAG, "No unowned devices found.");
239                 throw ESException("No unowned devices found.");
240             }
241         }
242
243         ESResult EnrolleeSecurity::performACLProvisioningForCloudServer(std::string cloudUuid)
244         {
245             ESResult res = ESResult::ES_ERROR;
246
247             OicUuid_t uuid;
248             convertStringToUUID(uuid, cloudUuid);
249
250             // Need to discover Owned device in a given network, again
251             OC::DeviceList_t pOwnedDevList;
252             std::shared_ptr< OC::OCSecureResource > ownedDevice = NULL;
253
254             pOwnedDevList.clear();
255
256             OCStackResult result;
257
258             result = OCSecure::discoverOwnedDevices(ES_SEC_DISCOVERY_TIMEOUT,
259                     pOwnedDevList);
260             if (result != OC_STACK_OK)
261             {
262                 OIC_LOG(ERROR, ENROLEE_SECURITY_TAG, "Owned Discovery failed.");
263                 //Throw exception
264                 throw ESPlatformException(result);
265             }
266             else if (pOwnedDevList.size())
267             {
268                 OIC_LOG_V(DEBUG, ENROLEE_SECURITY_TAG, "Found owned devices. Count =%d",
269                         pOwnedDevList.size());
270                 ownedDevice = getEnrollee(pOwnedDevList);
271
272                 if (!ownedDevice)
273                 {
274                     OIC_LOG(DEBUG, ENROLEE_SECURITY_TAG, "Not found owned devices.");
275                     return res;
276                 }
277             }
278             else
279             {
280                 OIC_LOG(DEBUG, ENROLEE_SECURITY_TAG, "Not found owned devices.");
281                 return res;
282             }
283
284             // Create Acl for Cloud Server to be provisioned to Enrollee
285             OicSecAcl_t* acl = createAcl(uuid);
286             if(!acl)
287             {
288                 OIC_LOG(ERROR, ENROLEE_SECURITY_TAG, "createAcl error return");
289                 return res;
290             }
291
292             OC::ResultCallBack aclProvisioningCb = std::bind(
293                             &EnrolleeSecurity::ACLProvisioningCb, this, std::placeholders::_1,
294                             std::placeholders::_2);
295             // ACL provisioning to Enrollee
296             OCStackResult rst = ownedDevice->provisionACL(acl, aclProvisioningCb);
297             if(OC_STACK_OK != rst)
298             {
299                 OIC_LOG_V(ERROR, ENROLEE_SECURITY_TAG, "OCProvisionACL API error: %d", rst);
300                 return res;
301             }
302
303             std::unique_lock<std::mutex> lck(m_mtx);
304             m_cond.wait_for(lck, std::chrono::seconds(ES_SEC_DISCOVERY_TIMEOUT));
305
306             if(aclResult)
307             {
308                 res = ESResult::ES_OK;
309             }
310
311             return res;
312         }
313
314         std::string EnrolleeSecurity::getUUID() const
315         {
316             return m_ocResource->sid();
317         };
318
319         OicSecAcl_t* EnrolleeSecurity::createAcl(OicUuid_t cloudUuid)
320         {
321             // allocate memory for |acl| struct
322             OicSecAcl_t* acl = (OicSecAcl_t*) OICCalloc(1, sizeof(OicSecAcl_t));
323             if(!acl)
324             {
325                 OIC_LOG(DEBUG, ENROLEE_SECURITY_TAG, "createAcl: OICCalloc error return");
326                 return NULL;  // not need to 'goto' |ERROR| before allocating |acl|
327             }
328             OicSecAce_t* ace = (OicSecAce_t*) OICCalloc(1, sizeof(OicSecAce_t));
329             if(!ace)
330             {
331                 OIC_LOG(DEBUG, ENROLEE_SECURITY_TAG,  "createAcl: OICCalloc error return");
332                 return NULL;  // not need to 'goto' |ERROR| before allocating |acl|
333             }
334             LL_APPEND(acl->aces, ace);
335
336             memcpy(&ace->subjectuuid, &cloudUuid, UUID_LENGTH);
337
338             OicSecRsrc_t* rsrc = (OicSecRsrc_t*)OICCalloc(1, sizeof(OicSecRsrc_t));
339             if(!rsrc)
340             {
341                 OIC_LOG(DEBUG, ENROLEE_SECURITY_TAG, "createAcl: OICCalloc error return");
342                 OCDeleteACLList(acl);
343                 return NULL;
344             }
345
346             char href[] = "*";
347             size_t len = strlen(href)+1;  // '1' for null termination
348             rsrc->href = (char*) OICCalloc(len, sizeof(char));
349             if(!rsrc->href)
350             {
351                 OIC_LOG(DEBUG, ENROLEE_SECURITY_TAG,  "createAcl: OICCalloc error return");
352                 OCDeleteACLList(acl);
353                 return NULL;
354             }
355             OICStrcpy(rsrc->href, len, href);
356
357             size_t arrLen = 1;
358             rsrc->typeLen = arrLen;
359             rsrc->types = (char**)OICCalloc(arrLen, sizeof(char*));
360             rsrc->interfaces = (char**)OICCalloc(arrLen, sizeof(char*));
361             rsrc->types[0] = OICStrdup("rt");   // ignore
362             rsrc->interfaces[0] = OICStrdup("if");  // ignore
363
364             LL_APPEND(ace->resources, rsrc);
365
366             ace->permission = 31;   // R/W/U/D
367
368             return acl;
369         }
370
371         void EnrolleeSecurity::ACLProvisioningCb(PMResultList_t *result, int hasError)
372         {
373             if (hasError)
374             {
375                OIC_LOG(DEBUG, ENROLEE_SECURITY_TAG, "Error in provisioning operation!");
376                aclResult = false;
377             }
378             else
379             {
380                OIC_LOG(DEBUG, ENROLEE_SECURITY_TAG, "Received provisioning results: ");
381
382                std::string devUuid;
383                for (unsigned int i = 0; i < result->size(); i++)
384                {
385                    convertUUIDToString(result->at(i).deviceId, devUuid);
386                    OIC_LOG_V(DEBUG, ENROLEE_SECURITY_TAG, "Result is = %d  for device %s",
387                                                            result->at(i).res, devUuid.c_str());
388                }
389                delete result;
390                aclResult = true;
391             }
392             m_cond.notify_all();
393         }
394     }
395 }