Security code comments which are doxygen compliant
[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     CborError 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 = { {.ptr = NULL }, .end = 0 };
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     *size = encoder.ptr - outPayload;
165     *payload = outPayload;
166     ret = OC_STACK_OK;
167
168 exit:
169     if ((CborErrorOutOfMemory == cborEncoderResult) && (cborLen < CBOR_MAX_SIZE))
170     {
171         // reallocate and try again!
172         OICFree(outPayload);
173         // Since the allocated initial memory failed, double the memory.
174         cborLen += encoder.ptr - encoder.end;
175         cborEncoderResult = CborNoError;
176         ret = PstatToCBORPayload(pstat, payload, &cborLen);
177     }
178
179     if ((CborNoError != cborEncoderResult) || (OC_STACK_OK != ret))
180     {
181         OICFree(outPayload);
182         outPayload = NULL;
183         *payload = NULL;
184         *size = 0;
185         ret = OC_STACK_ERROR;
186     }
187
188     return ret;
189 }
190
191 OCStackResult CBORPayloadToPstat(const uint8_t *cborPayload, const size_t size,
192                                  OicSecPstat_t **secPstat)
193 {
194     if (NULL == cborPayload || NULL == secPstat || NULL != *secPstat)
195     {
196         return OC_STACK_INVALID_PARAM;
197     }
198
199     OCStackResult ret = OC_STACK_ERROR;
200     *secPstat = NULL;
201
202     CborValue pstatCbor = { .parser = NULL };
203     CborParser parser = { .end = NULL };
204     CborError cborFindResult = CborNoError;
205     int cborLen = size;
206     if (0 == size)
207     {
208         cborLen = CBOR_SIZE;
209     }
210     cbor_parser_init(cborPayload, cborLen, 0, &parser, &pstatCbor);
211     CborValue pstatMap = { .parser = NULL } ;
212     OicSecPstat_t *pstat = NULL;
213     char *name = NULL;
214     cborFindResult = cbor_value_enter_container(&pstatCbor, &pstatMap);
215     VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding PSTAT Map.");
216
217     pstat = (OicSecPstat_t *)OICCalloc(1, sizeof(OicSecPstat_t));
218     VERIFY_NON_NULL(TAG, pstat, ERROR);
219
220     while (cbor_value_is_valid(&pstatMap))
221     {
222         size_t len = 0;
223         cborFindResult = cbor_value_dup_text_string(&pstatMap, &name, &len, NULL);
224         VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding PSTAT Name Value.");
225         cborFindResult = cbor_value_advance(&pstatMap);
226         VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Advancing PSTAT MAP.");
227
228         CborType type = cbor_value_get_type(&pstatMap);
229
230         if (0 == strcmp(OIC_JSON_ISOP_NAME, name))
231         {
232             cborFindResult = cbor_value_get_boolean(&pstatMap, &pstat->isOp);
233             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding ISOP Name.");
234         }
235
236         if (0 == strcmp(OIC_JSON_DEVICE_ID_NAME, name))
237         {
238             uint8_t *subjectId = NULL;
239             cborFindResult = cbor_value_dup_byte_string(&pstatMap, &subjectId, &len, NULL);
240             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding SubjectId.");
241             memcpy(pstat->deviceID.id, subjectId, len);
242             OICFree(subjectId);
243         }
244
245         if (0 == strcmp(OIC_JSON_COMMIT_HASH_NAME, name))
246         {
247             cborFindResult = cbor_value_get_int(&pstatMap, (int *) &pstat->commitHash);
248             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding CommitHash.");
249         }
250
251         if (0 == strcmp(OIC_JSON_CM_NAME, name))
252         {
253             cborFindResult = cbor_value_get_int(&pstatMap, (int *) &pstat->cm);
254             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding CM Name.");
255         }
256
257         if (0 == strcmp(OIC_JSON_OM_NAME, name))
258         {
259             cborFindResult = cbor_value_get_int(&pstatMap, (int *) &pstat->om);
260             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding OM Name.");
261         }
262
263         if (0 == strcmp(OIC_JSON_SM_NAME, name))
264         {
265             CborValue sm = { .parser = NULL };
266
267             cborFindResult = cbor_value_get_array_length(&pstatMap, &pstat->smLen);
268             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding SM Len Array.");
269             VERIFY_SUCCESS(TAG, pstat->smLen != 0, ERROR);
270
271             pstat->sm = (OicSecDpom_t *)OICCalloc(pstat->smLen, sizeof(OicSecDpom_t));
272             VERIFY_NON_NULL(TAG, pstat->sm, ERROR);
273
274             cborFindResult = cbor_value_enter_container(&pstatMap, &sm);
275             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding SM Container.");
276
277             int i = 0;
278             while (cbor_value_is_valid(&sm))
279             {
280                 cborFindResult = cbor_value_get_int(&sm, (int *)&pstat->sm[i++]);
281                 VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding SM Value.");
282                 cborFindResult = cbor_value_advance(&sm);
283                 VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding SM Array.");
284             }
285         }
286         if (CborMapType != type && cbor_value_is_valid(&pstatMap))
287         {
288             cborFindResult = cbor_value_advance(&pstatMap);
289             VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed Finding PSTAT MAP.");
290         }
291         OICFree(name);
292         name = NULL;
293     }
294
295     *secPstat = pstat;
296     ret = OC_STACK_OK;
297
298 exit:
299     if (CborNoError != cborFindResult)
300     {
301         OIC_LOG(ERROR, TAG, "CBORPayloadToPstat failed");
302         DeletePstatBinData(pstat);
303         pstat = NULL;
304         ret = OC_STACK_ERROR;
305     }
306     if (name)
307     {
308         OICFree(name);
309     }
310     return ret;
311 }
312
313 /**
314  * Function to update persistent storage
315  */
316 static bool UpdatePersistentStorage(OicSecPstat_t *pstat)
317 {
318     bool bRet = false;
319
320     size_t size = 0;
321     uint8_t *cborPayload = NULL;
322     OCStackResult ret = PstatToCBORPayload(pstat, &cborPayload, &size);
323     if (OC_STACK_OK == ret)
324     {
325         if (OC_STACK_OK == UpdateSecureResourceInPS(OIC_JSON_PSTAT_NAME, cborPayload, size))
326         {
327             bRet = true;
328         }
329         OICFree(cborPayload);
330     }
331
332     return bRet;
333 }
334
335
336 /**
337  * The entity handler determines how to process a GET request.
338  */
339 static OCEntityHandlerResult HandlePstatGetRequest (const OCEntityHandlerRequest * ehRequest)
340 {
341     OIC_LOG(INFO, TAG, "HandlePstatGetRequest  processing GET request");
342
343     // Convert ACL data into CBOR for transmission
344     size_t size = 0;
345     uint8_t *payload = NULL;
346     OCStackResult res = PstatToCBORPayload(gPstat, &payload, &size);
347
348     // A device should always have a default pstat. Therefore, payload should never be NULL.
349     OCEntityHandlerResult ehRet = (res == OC_STACK_OK) ? OC_EH_OK : OC_EH_ERROR;
350
351     // Send response payload to request originator
352     SendSRMCBORResponse(ehRequest, ehRet, payload);
353     OICFree(payload);
354     return ehRet;
355 }
356
357 /**
358  * The entity handler determines how to process a POST request.
359  * Per the REST paradigm, POST can also be used to update representation of existing
360  * resource or create a new resource.
361  * For pstat, it updates only tm and om.
362  */
363 static OCEntityHandlerResult HandlePstatPutRequest(const OCEntityHandlerRequest *ehRequest)
364 {
365     OCEntityHandlerResult ehRet = OC_EH_ERROR;
366     OIC_LOG(INFO, TAG, "HandlePstatPutRequest  processing PUT request");
367     OicSecPstat_t *pstat = NULL;
368
369     if (ehRequest->resource)
370     {
371         uint8_t *payload = ((OCSecurityPayload *) ehRequest->payload)->securityData1;
372         VERIFY_NON_NULL(TAG, payload, ERROR);
373
374         OCStackResult ret = CBORPayloadToPstat(payload, CBOR_SIZE, &pstat);
375         OICFree(payload);
376         VERIFY_NON_NULL(TAG, pstat, ERROR);
377         if (OC_STACK_OK == ret)
378         {
379             if (pstat->tm)
380             {
381                 gPstat->tm = pstat->tm;
382                 if(0 == pstat->tm && gPstat->commitHash == pstat->commitHash)
383                 {
384                     gPstat->isOp = true;
385                     gPstat->cm = NORMAL;
386                     OIC_LOG (INFO, TAG, "CommitHash is valid and isOp is TRUE");
387                 }
388                 else
389                 {
390                     OIC_LOG(DEBUG, TAG, "CommitHash is not valid");
391                 }
392             }
393             if (pstat->om && gPstat)
394             {
395                 /*
396                  * Check if the operation mode is in the supported provisioning services
397                  * operation mode list.
398                  */
399                 for(size_t i=0; i< gPstat->smLen; i++)
400                 {
401                     if(gPstat->sm[i] == pstat->om)
402                     {
403                         gPstat->om = pstat->om;
404                         break;
405                     }
406                 }
407             }
408             // Convert pstat data into CBOR for update to persistent storage
409             if(UpdatePersistentStorage(gPstat))
410             {
411                 ehRet = OC_EH_OK;
412             }
413         }
414
415         OICFree(payload);
416     }
417  exit:
418     if(OC_EH_OK != ehRet)
419     {
420         /*
421           * If some error is occured while ownership transfer,
422           * ownership transfer related resource should be revert back to initial status.
423           */
424         RestoreDoxmToInitState();
425         RestorePstatToInitState();
426     }
427
428     //Send payload to request originator
429     if (OC_STACK_OK != SendSRMCBORResponse(ehRequest, ehRet, NULL))
430     {
431         OIC_LOG (ERROR, TAG, "SendSRMResponse failed in HandlePstatPostRequest");
432     }
433     DeletePstatBinData(pstat);
434     return ehRet;
435 }
436
437 /**
438  * This internal method is the entity handler for pstat resources.
439  */
440  OCEntityHandlerResult PstatEntityHandler(OCEntityHandlerFlag flag,
441                                           OCEntityHandlerRequest * ehRequest,
442                                           void *callbackParam)
443 {
444     (void)callbackParam;
445     OCEntityHandlerResult ehRet = OC_EH_ERROR;
446     // This method will handle REST request (GET/POST) for /oic/sec/pstat
447     if (flag & OC_REQUEST_FLAG)
448     {
449         OIC_LOG(INFO, TAG, "Flag includes OC_REQUEST_FLAG");
450         switch (ehRequest->method)
451         {
452             case OC_REST_GET:
453                 ehRet = HandlePstatGetRequest(ehRequest);
454                 break;
455             case OC_REST_PUT:
456                 ehRet = HandlePstatPutRequest(ehRequest);
457                 break;
458             default:
459                 ehRet = OC_EH_ERROR;
460                 SendSRMCBORResponse(ehRequest, ehRet, NULL);
461                 break;
462         }
463     }
464     return ehRet;
465 }
466
467 /**
468  * This internal method is used to create '/oic/sec/pstat' resource.
469  */
470  OCStackResult CreatePstatResource()
471 {
472     OCStackResult ret = OCCreateResource(&gPstatHandle,
473                                          OIC_RSRC_TYPE_SEC_PSTAT,
474                                          OIC_MI_DEF,
475                                          OIC_RSRC_PSTAT_URI,
476                                          PstatEntityHandler,
477                                          NULL,
478                                          OC_RES_PROP_NONE);
479
480     if (OC_STACK_OK != ret)
481     {
482         OIC_LOG(FATAL, TAG, "Unable to instantiate pstat resource");
483         DeInitPstatResource();
484     }
485     return ret;
486 }
487
488 /**
489  * Get the default value.
490  *
491  * @return the gDefaultPstat pointer.
492  */
493 static OicSecPstat_t* GetPstatDefault()
494 {
495     return &gDefaultPstat;
496 }
497
498 OCStackResult InitPstatResource()
499 {
500     OCStackResult ret = OC_STACK_ERROR;
501
502     // Read Pstat resource from PS
503     uint8_t *data = NULL;
504     size_t size = 0;
505     ret = GetSecureVirtualDatabaseFromPS(OIC_JSON_PSTAT_NAME, &data, &size);
506     // If database read failed
507     if (OC_STACK_OK != ret)
508     {
509         OIC_LOG (DEBUG, TAG, "ReadSVDataFromPS failed");
510     }
511     if (data)
512     {
513         // Read ACL resource from PS
514         ret = CBORPayloadToPstat(data, size, &gPstat);
515     }
516     /*
517      * If SVR database in persistent storage got corrupted or
518      * is not available for some reason, a default pstat is created
519      * which allows user to initiate pstat provisioning again.
520      */
521     if ((OC_STACK_OK != ret) || !data || !gPstat)
522     {
523         gPstat = GetPstatDefault();
524     }
525     VERIFY_NON_NULL(TAG, gPstat, FATAL);
526
527     // Instantiate 'oic.sec.pstat'
528     ret = CreatePstatResource();
529
530 exit:
531     OICFree(data);
532     if (OC_STACK_OK != ret)
533     {
534         DeInitPstatResource();
535     }
536     return ret;
537 }
538
539 OCStackResult DeInitPstatResource()
540 {
541     if (gPstat != &gDefaultPstat)
542     {
543         DeletePstatBinData(gPstat);
544         gPstat = NULL;
545     }
546     return OCDeleteResource(gPstatHandle);
547 }
548
549 /**
550  * Function to restore pstat resurce to initial status.
551  * This function will use in case of error while ownership transfer
552  */
553 void RestorePstatToInitState()
554 {
555     if(gPstat)
556     {
557         OIC_LOG(INFO, TAG, "PSTAT resource will revert back to initial status.");
558
559         gPstat->cm = NORMAL;
560         gPstat->tm = NORMAL;
561         gPstat->om = SINGLE_SERVICE_CLIENT_DRIVEN;
562         if(gPstat->sm && 0 < gPstat->smLen)
563         {
564             gPstat->sm[0] = SINGLE_SERVICE_CLIENT_DRIVEN;
565         }
566
567         if (!UpdatePersistentStorage(gPstat))
568         {
569             OIC_LOG(ERROR, TAG, "Failed to revert DOXM in persistent storage");
570         }
571     }
572 }