Security CBOR conversion
[platform/upstream/iotivity.git] / resource / csdk / security / src / pstatresource.c
1 //******************************************************************
2 //
3 // Copyright 2015 Intel Mobile Communications GmbH 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 <stdlib.h>
22 #include <string.h>
23
24 #include "ocstack.h"
25 #include "oic_malloc.h"
26 #include "ocpayload.h"
27 #include "payload_logging.h"
28 #include "resourcemanager.h"
29 #include "pstatresource.h"
30 #include "doxmresource.h"
31 #include "psinterface.h"
32 #include "srmresourcestrings.h"
33 #include "srmutility.h"
34
35 #define TAG  "SRM-PSTAT"
36
37 /** Default cbor payload size. This value is increased in case of CborErrorOutOfMemory.
38  * The value of payload size is increased until reaching below max cbor size. */
39 static const uint8_t CBOR_SIZE = 255;
40
41 // Max cbor size payload.
42 static const uint16_t CBOR_MAX_SIZE = 4400;
43
44 // PSTAT Map size - Number of mandatory items
45 static const uint8_t PSTAT_MAP_SIZE = 7;
46
47 static OicSecDpom_t gSm = SINGLE_SERVICE_CLIENT_DRIVEN;
48 static OicSecPstat_t gDefaultPstat =
49 {
50     false,                                    // bool isOwned
51     (OicSecDpm_t)(TAKE_OWNER | BOOTSTRAP_SERVICE | SECURITY_MANAGEMENT_SERVICES |
52     PROVISION_CREDENTIALS | PROVISION_ACLS),   // OicSecDpm_t cm
53     (OicSecDpm_t)(TAKE_OWNER | BOOTSTRAP_SERVICE | SECURITY_MANAGEMENT_SERVICES |
54     PROVISION_CREDENTIALS | PROVISION_ACLS),   // OicSecDpm_t tm
55     {.id = {0}},                              // OicUuid_t deviceID
56     SINGLE_SERVICE_CLIENT_DRIVEN,             // OicSecDpom_t om */
57     1,                                        // the number of elts in Sms
58     &gSm,                                     // OicSecDpom_t *sm
59     0,                                        // uint16_t commitHash
60 };
61
62 static OicSecPstat_t    *gPstat = NULL;
63
64 static OCResourceHandle gPstatHandle = NULL;
65
66 void DeletePstatBinData(OicSecPstat_t* pstat)
67 {
68     if (pstat)
69     {
70         //Clean 'supported modes' field
71         OICFree(pstat->sm);
72
73         //Clean pstat itself
74         OICFree(pstat);
75     }
76 }
77
78 OCStackResult PstatToCBORPayload(const OicSecPstat_t *pstat, uint8_t **payload, size_t *size)
79 {
80     if (NULL == pstat || NULL == payload || NULL != *payload || NULL == size)
81     {
82         return OC_STACK_INVALID_PARAM;
83     }
84
85     size_t cborLen = *size;
86     if (0 == cborLen)
87     {
88         cborLen = CBOR_SIZE;
89     }
90
91     *payload = NULL;
92     *size = 0;
93
94     OCStackResult ret = OC_STACK_ERROR;
95
96     CborEncoder encoder = { {.ptr = NULL }, .end = 0 };
97     CborEncoder pstatMap = { {.ptr = NULL }, .end = 0 };
98
99     int64_t cborEncoderResult = CborNoError;
100
101     uint8_t *outPayload = (uint8_t *)OICCalloc(1, cborLen);
102     VERIFY_NON_NULL(TAG, outPayload, ERROR);
103     cbor_encoder_init(&encoder, outPayload, cborLen, 0);
104
105     cborEncoderResult |= cbor_encoder_create_map(&encoder, &pstatMap, PSTAT_MAP_SIZE);
106     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding Pstat Map.");
107
108     cborEncoderResult |= cbor_encode_text_string(&pstatMap, OIC_JSON_ISOP_NAME,
109         strlen(OIC_JSON_ISOP_NAME));
110     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding ISOP Name Tag.");
111     cborEncoderResult |= cbor_encode_boolean(&pstatMap, pstat->isOp);
112     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding ISOP Name Value.");
113
114     cborEncoderResult |= cbor_encode_text_string(&pstatMap, OIC_JSON_DEVICE_ID_NAME,
115         strlen(OIC_JSON_DEVICE_ID_NAME));
116     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding Device Id Tag.");
117     cborEncoderResult |= cbor_encode_byte_string(&pstatMap, (uint8_t *)pstat->deviceID.id,
118                                                 sizeof(pstat->deviceID.id));
119     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding Device Id Value.");
120
121     cborEncoderResult |= cbor_encode_text_string(&pstatMap, OIC_JSON_COMMIT_HASH_NAME,
122         strlen(OIC_JSON_COMMIT_HASH_NAME));
123     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding Commit Hash Tag.");
124     cborEncoderResult |= cbor_encode_int(&pstatMap, pstat->commitHash);
125     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding Commit Hash Value.");
126
127     cborEncoderResult |= cbor_encode_text_string(&pstatMap, OIC_JSON_CM_NAME,
128         strlen(OIC_JSON_CM_NAME));
129     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding CM Name Tag.");
130     cborEncoderResult |= cbor_encode_int(&pstatMap, pstat->cm);
131     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding CM Name Value.");
132
133     cborEncoderResult |= cbor_encode_text_string(&pstatMap, OIC_JSON_TM_NAME,
134         strlen(OIC_JSON_TM_NAME));
135     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding TM Name Tag.");
136     cborEncoderResult |= cbor_encode_int(&pstatMap, pstat->tm);
137     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding TM Name Value.");
138
139     cborEncoderResult |= cbor_encode_text_string(&pstatMap, OIC_JSON_OM_NAME,
140         strlen(OIC_JSON_OM_NAME));
141     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding OM Name Tag.");
142     cborEncoderResult |= cbor_encode_int(&pstatMap, pstat->om);
143     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding OM Name Value.");
144
145     cborEncoderResult |= cbor_encode_text_string(&pstatMap, OIC_JSON_SM_NAME,
146         strlen(OIC_JSON_SM_NAME));
147     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding SM Name Tag.");
148     {
149         CborEncoder sm;
150         cborEncoderResult |= cbor_encoder_create_array(&pstatMap, &sm, pstat->smLen);
151         VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding SM Array.");
152
153         for (size_t i = 0; i < pstat->smLen; i++)
154         {
155             cborEncoderResult |= cbor_encode_int(&sm, pstat->sm[i]);
156             VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding SM Value in Array.");
157         }
158         cborEncoderResult |= cbor_encoder_close_container(&pstatMap, &sm);
159         VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Closing SM Array.");
160     }
161     cborEncoderResult |= cbor_encoder_close_container(&encoder, &pstatMap);
162     VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed Adding Closing PSTAT Map.");
163
164     if (CborNoError == cborEncoderResult)
165     {
166         *size = encoder.ptr - outPayload;
167         *payload = outPayload;
168         ret = OC_STACK_OK;
169     }
170 exit:
171     if ((CborErrorOutOfMemory == cborEncoderResult) && (cborLen < CBOR_MAX_SIZE))
172     {
173         // reallocate and try again!
174         OICFree(outPayload);
175         // Since the allocated initial memory failed, double the memory.
176         cborLen += encoder.ptr - encoder.end;
177         cborEncoderResult = CborNoError;
178         ret = PstatToCBORPayload(pstat, payload, &cborLen);
179         if (OC_STACK_OK == ret)
180         {
181             *size = cborLen;
182         }
183     }
184
185     if ((CborNoError != cborEncoderResult) || (OC_STACK_OK != ret))
186     {
187         OICFree(outPayload);
188         outPayload = NULL;
189         *payload = NULL;
190         *size = 0;
191         ret = OC_STACK_ERROR;
192     }
193
194     return ret;
195 }
196
197 OCStackResult CBORPayloadToPstat(const uint8_t *cborPayload, const size_t size,
198                                  OicSecPstat_t **secPstat)
199 {
200     if (NULL == cborPayload || NULL == secPstat || NULL != *secPstat)
201     {
202         return OC_STACK_INVALID_PARAM;
203     }
204
205     OCStackResult ret = OC_STACK_ERROR;
206     *secPstat = NULL;
207
208     CborValue pstatCbor;
209     CborParser parser;
210     CborError cborFindResult = CborNoError;
211     int cborLen = size;
212     size_t len = 0;
213     if (0 == size)
214     {
215         cborLen = CBOR_SIZE;
216     }
217     cbor_parser_init(cborPayload, cborLen, 0, &parser, &pstatCbor);
218     CborValue pstatMap;
219
220     OicSecPstat_t *pstat = NULL;
221     cborFindResult = cbor_value_enter_container(&pstatCbor, &pstatMap);
222     VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding PSTAT Map.");
223
224     pstat = (OicSecPstat_t *)OICCalloc(1, sizeof(OicSecPstat_t));
225     VERIFY_NON_NULL(TAG, pstat, ERROR);
226
227     cborFindResult = cbor_value_map_find_value(&pstatCbor, OIC_JSON_ISOP_NAME, &pstatMap);
228     if (CborNoError == cborFindResult && cbor_value_is_boolean(&pstatMap))
229     {
230         cborFindResult = cbor_value_get_boolean(&pstatMap, &pstat->isOp);
231         VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding isOp Value.");
232     }
233
234     cborFindResult = cbor_value_map_find_value(&pstatCbor, OIC_JSON_DEVICE_ID_NAME, &pstatMap);
235     if (CborNoError == cborFindResult && cbor_value_is_byte_string(&pstatMap))
236     {
237         uint8_t *subjectId = NULL;
238         cborFindResult = cbor_value_dup_byte_string(&pstatMap, &subjectId, &len, NULL);
239         VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding subjectId Value.");
240         memcpy(pstat->deviceID.id, subjectId, len);
241         OICFree(subjectId);
242     }
243
244     cborFindResult = cbor_value_map_find_value(&pstatCbor, OIC_JSON_COMMIT_HASH_NAME, &pstatMap);
245     if (CborNoError == cborFindResult && cbor_value_is_integer(&pstatMap))
246     {
247         cborFindResult = cbor_value_get_int(&pstatMap, (int *) &pstat->commitHash);
248         VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding commitHash.");
249     }
250
251     cborFindResult = cbor_value_map_find_value(&pstatCbor, OIC_JSON_CM_NAME, &pstatMap);
252     if (CborNoError == cborFindResult && cbor_value_is_integer(&pstatMap))
253     {
254         cborFindResult = cbor_value_get_int(&pstatMap, (int *) &pstat->cm);
255         VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding CM.");
256     }
257
258     cborFindResult = cbor_value_map_find_value(&pstatCbor, OIC_JSON_OM_NAME, &pstatMap);
259     if (CborNoError == cborFindResult && cbor_value_is_integer(&pstatMap))
260     {
261         cborFindResult = cbor_value_get_int(&pstatMap, (int *) &pstat->om);
262         VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding OM.");
263     }
264
265     cborFindResult = cbor_value_map_find_value(&pstatCbor, OIC_JSON_SM_NAME, &pstatMap);
266     if (CborNoError == cborFindResult && cbor_value_is_array(&pstatMap))
267     {
268         CborValue sm = { .parser = NULL };
269         cborFindResult = cbor_value_get_array_length(&pstatMap, &pstat->smLen);
270         VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding Array Len.");
271
272         pstat->sm = (OicSecDpom_t *)OICCalloc(pstat->smLen, sizeof(OicSecDpom_t));
273         VERIFY_NON_NULL(TAG, pstat->sm, ERROR);
274
275         cborFindResult = cbor_value_enter_container(&pstatMap, &sm);
276         VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Entering SM.");
277
278         int i = 0;
279         while (cbor_value_is_valid(&sm))
280         {
281             cborFindResult = cbor_value_get_int(&sm, (int *)&pstat->sm[i++]);
282             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding SM.");
283             cborFindResult = cbor_value_advance(&sm);
284             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Closing SM.");
285         }
286     }
287
288     *secPstat = pstat;
289     ret = OC_STACK_OK;
290
291 exit:
292     if (CborNoError != cborFindResult)
293     {
294         OIC_LOG(ERROR, TAG, "CBORPayloadToPstat failed");
295         DeletePstatBinData(pstat);
296         pstat = NULL;
297         ret = OC_STACK_ERROR;
298     }
299
300     return ret;
301 }
302
303 /**
304  * Function to update persistent storage
305  */
306 static bool UpdatePersistentStorage(OicSecPstat_t *pstat)
307 {
308     bool bRet = false;
309
310     size_t size = 0;
311     uint8_t *cborPayload = NULL;
312     OCStackResult ret = PstatToCBORPayload(pstat, &cborPayload, &size);
313     if (OC_STACK_OK == ret)
314     {
315         if (OC_STACK_OK == UpdateSecureResourceInPS(OIC_JSON_PSTAT_NAME, cborPayload, size))
316         {
317             bRet = true;
318         }
319         OICFree(cborPayload);
320     }
321
322     return bRet;
323 }
324
325
326 /**
327  * The entity handler determines how to process a GET request.
328  */
329 static OCEntityHandlerResult HandlePstatGetRequest (const OCEntityHandlerRequest * ehRequest)
330 {
331     OIC_LOG(INFO, TAG, "HandlePstatGetRequest  processing GET request");
332
333     // Convert ACL data into CBOR for transmission
334     size_t size = 0;
335     uint8_t *payload = NULL;
336     OCStackResult res = PstatToCBORPayload(gPstat, &payload, &size);
337
338     // A device should always have a default pstat. Therefore, payload should never be NULL.
339     OCEntityHandlerResult ehRet = (res == OC_STACK_OK) ? OC_EH_OK : OC_EH_ERROR;
340
341     // Send response payload to request originator
342     SendSRMCBORResponse(ehRequest, ehRet, payload, size);
343     OICFree(payload);
344     return ehRet;
345 }
346
347 /**
348  * The entity handler determines how to process a POST request.
349  * Per the REST paradigm, POST can also be used to update representation of existing
350  * resource or create a new resource.
351  * For pstat, it updates only tm and om.
352  */
353 static OCEntityHandlerResult HandlePstatPutRequest(const OCEntityHandlerRequest *ehRequest)
354 {
355     OCEntityHandlerResult ehRet = OC_EH_ERROR;
356     OIC_LOG(INFO, TAG, "HandlePstatPutRequest  processing PUT request");
357     OicSecPstat_t *pstat = NULL;
358
359     if (ehRequest->resource)
360     {
361         uint8_t *payload = ((OCSecurityPayload *) ehRequest->payload)->securityData1;
362         size_t size = ((OCSecurityPayload *) ehRequest->payload)->payloadSize;
363         VERIFY_NON_NULL(TAG, payload, ERROR);
364
365         OCStackResult ret = CBORPayloadToPstat(payload, size, &pstat);
366         OICFree(payload);
367         VERIFY_NON_NULL(TAG, pstat, ERROR);
368         if (OC_STACK_OK == ret)
369         {
370             if (pstat->tm != NORMAL)
371             {
372                 gPstat->tm = pstat->tm;
373                 if(0 == pstat->tm && gPstat->commitHash == pstat->commitHash)
374                 {
375                     gPstat->isOp = true;
376                     gPstat->cm = NORMAL;
377                     OIC_LOG (INFO, TAG, "CommitHash is valid and isOp is TRUE");
378                 }
379                 else
380                 {
381                     OIC_LOG(DEBUG, TAG, "CommitHash is not valid");
382                 }
383             }
384             if (pstat->om != MULTIPLE_SERVICE_SERVER_DRIVEN && gPstat)
385             {
386                 /*
387                  * Check if the operation mode is in the supported provisioning services
388                  * operation mode list.
389                  */
390                 for (size_t i=0; i< gPstat->smLen; i++)
391                 {
392                     if(gPstat->sm[i] == pstat->om)
393                     {
394                         gPstat->om = pstat->om;
395                         break;
396                     }
397                 }
398             }
399             // Convert pstat data into CBOR for update to persistent storage
400             if (UpdatePersistentStorage(gPstat))
401             {
402                 ehRet = OC_EH_OK;
403             }
404         }
405     }
406  exit:
407     if(OC_EH_OK != ehRet)
408     {
409         /*
410           * If some error is occured while ownership transfer,
411           * ownership transfer related resource should be revert back to initial status.
412           */
413         RestoreDoxmToInitState();
414         RestorePstatToInitState();
415     }
416
417     //Send payload to request originator
418     if(OC_STACK_OK != SendSRMCBORResponse(ehRequest, ehRet, NULL, 0))
419     {
420         OIC_LOG (ERROR, TAG, "SendSRMResponse failed in HandlePstatPostRequest");
421     }
422     DeletePstatBinData(pstat);
423     return ehRet;
424 }
425
426 /**
427  * This internal method is the entity handler for pstat resources.
428  */
429  OCEntityHandlerResult PstatEntityHandler(OCEntityHandlerFlag flag,
430                                           OCEntityHandlerRequest * ehRequest,
431                                           void *callbackParam)
432 {
433     (void)callbackParam;
434     OCEntityHandlerResult ehRet = OC_EH_ERROR;
435     // This method will handle REST request (GET/POST) for /oic/sec/pstat
436     if (flag & OC_REQUEST_FLAG)
437     {
438         OIC_LOG(INFO, TAG, "Flag includes OC_REQUEST_FLAG");
439         switch (ehRequest->method)
440         {
441             case OC_REST_GET:
442                 ehRet = HandlePstatGetRequest(ehRequest);
443                 break;
444             case OC_REST_PUT:
445                 ehRet = HandlePstatPutRequest(ehRequest);
446                 break;
447             default:
448                 ehRet = OC_EH_ERROR;
449                 SendSRMCBORResponse(ehRequest, ehRet, NULL, 0);
450                 break;
451         }
452     }
453     return ehRet;
454 }
455
456 /**
457  * This internal method is used to create '/oic/sec/pstat' resource.
458  */
459  OCStackResult CreatePstatResource()
460 {
461     OCStackResult ret = OCCreateResource(&gPstatHandle,
462                                          OIC_RSRC_TYPE_SEC_PSTAT,
463                                          OIC_MI_DEF,
464                                          OIC_RSRC_PSTAT_URI,
465                                          PstatEntityHandler,
466                                          NULL,
467                                          OC_RES_PROP_NONE);
468
469     if (OC_STACK_OK != ret)
470     {
471         OIC_LOG(FATAL, TAG, "Unable to instantiate pstat resource");
472         DeInitPstatResource();
473     }
474     return ret;
475 }
476
477 /**
478  * Get the default value.
479  *
480  * @return the gDefaultPstat pointer.
481  */
482 static OicSecPstat_t* GetPstatDefault()
483 {
484     return &gDefaultPstat;
485 }
486
487 OCStackResult InitPstatResource()
488 {
489     OCStackResult ret = OC_STACK_ERROR;
490
491     // Read Pstat resource from PS
492     uint8_t *data = NULL;
493     size_t size = 0;
494     ret = GetSecureVirtualDatabaseFromPS(OIC_JSON_PSTAT_NAME, &data, &size);
495     // If database read failed
496     if (OC_STACK_OK != ret)
497     {
498         OIC_LOG (DEBUG, TAG, "ReadSVDataFromPS failed");
499     }
500     if (data)
501     {
502         // Read ACL resource from PS
503         ret = CBORPayloadToPstat(data, size, &gPstat);
504         OICFree(data);
505     }
506     /*
507      * If SVR database in persistent storage got corrupted or
508      * is not available for some reason, a default pstat is created
509      * which allows user to initiate pstat provisioning again.
510      */
511     if ((OC_STACK_OK != ret) || !gPstat)
512     {
513         gPstat = GetPstatDefault();
514     }
515     VERIFY_NON_NULL(TAG, gPstat, FATAL);
516
517     // Instantiate 'oic.sec.pstat'
518     ret = CreatePstatResource();
519
520 exit:
521     if (OC_STACK_OK != ret)
522     {
523         DeInitPstatResource();
524     }
525     return ret;
526 }
527
528 OCStackResult DeInitPstatResource()
529 {
530     if (gPstat != &gDefaultPstat)
531     {
532         DeletePstatBinData(gPstat);
533         gPstat = NULL;
534     }
535     return OCDeleteResource(gPstatHandle);
536 }
537
538 /**
539  * Function to restore pstat resurce to initial status.
540  * This function will use in case of error while ownership transfer
541  */
542 void RestorePstatToInitState()
543 {
544     if(gPstat)
545     {
546         OIC_LOG(INFO, TAG, "PSTAT resource will revert back to initial status.");
547
548         gPstat->cm = NORMAL;
549         gPstat->tm = NORMAL;
550         gPstat->om = SINGLE_SERVICE_CLIENT_DRIVEN;
551         if(gPstat->sm && 0 < gPstat->smLen)
552         {
553             gPstat->sm[0] = SINGLE_SERVICE_CLIENT_DRIVEN;
554         }
555
556         if (!UpdatePersistentStorage(gPstat))
557         {
558             OIC_LOG(ERROR, TAG, "Failed to revert DOXM in persistent storage");
559         }
560     }
561 }