Modify OTM & SRM according to spec CR document.
[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 "ocstack.h"
22 #include "logger.h"
23 #include "oic_malloc.h"
24 #include "cJSON.h"
25 #include "resourcemanager.h"
26 #include "pstatresource.h"
27 #include "doxmresource.h"
28 #include "psinterface.h"
29 #include "utlist.h"
30 #include "base64.h"
31 #include "srmresourcestrings.h"
32 #include "srmutility.h"
33 #include <stdlib.h>
34 #include <string.h>
35
36 #define TAG  "SRM-PSTAT"
37
38 static OicSecDpom_t gSm = SINGLE_SERVICE_CLIENT_DRIVEN;
39 static OicSecPstat_t gDefaultPstat =
40 {
41     false,                                    // bool isOwned
42     (OicSecDpm_t)(TAKE_OWNER | BOOTSTRAP_SERVICE | SECURITY_MANAGEMENT_SERVICES |
43     PROVISION_CREDENTIALS | PROVISION_ACLS),   // OicSecDpm_t cm
44     (OicSecDpm_t)(TAKE_OWNER | BOOTSTRAP_SERVICE | SECURITY_MANAGEMENT_SERVICES |
45     PROVISION_CREDENTIALS | PROVISION_ACLS),   // OicSecDpm_t tm
46     {.id = {0}},                              // OicUuid_t deviceID
47     SINGLE_SERVICE_CLIENT_DRIVEN,             // OicSecDpom_t om */
48     1,                                        // the number of elts in Sms
49     &gSm,                                     // OicSecDpom_t *sm
50     0,                                        // uint16_t commitHash
51 };
52
53 static OicSecPstat_t    *gPstat = NULL;
54
55 static OCResourceHandle gPstatHandle = NULL;
56
57 void DeletePstatBinData(OicSecPstat_t* pstat)
58 {
59     if (pstat)
60     {
61         //Clean 'supported modes' field
62         OICFree(pstat->sm);
63
64         //Clean pstat itself
65         OICFree(pstat);
66     }
67 }
68
69 char * BinToPstatJSON(const OicSecPstat_t * pstat)
70 {
71     if(NULL == pstat)
72     {
73         return NULL;
74     }
75
76     cJSON *jsonPstat = NULL;
77     char *jsonStr = NULL;
78     cJSON *jsonSmArray = NULL;
79     char base64Buff[B64ENCODE_OUT_SAFESIZE(sizeof(((OicUuid_t*) 0)->id)) + 1] = {};
80     uint32_t outLen = 0;
81     B64Result b64Ret = B64_OK;
82
83     cJSON *jsonRoot = cJSON_CreateObject();
84     VERIFY_NON_NULL(TAG, jsonRoot, INFO);
85
86     cJSON_AddItemToObject(jsonRoot, OIC_JSON_PSTAT_NAME, jsonPstat=cJSON_CreateObject());
87     cJSON_AddBoolToObject(jsonPstat, OIC_JSON_ISOP_NAME, pstat->isOp);
88
89     b64Ret = b64Encode(pstat->deviceID.id,
90             sizeof(pstat->deviceID.id), base64Buff, sizeof(base64Buff), &outLen);
91     VERIFY_SUCCESS(TAG, b64Ret == B64_OK, ERROR);
92
93     cJSON_AddStringToObject(jsonPstat, OIC_JSON_DEVICE_ID_NAME, base64Buff);
94     cJSON_AddNumberToObject(jsonPstat, OIC_JSON_COMMIT_HASH_NAME, pstat->commitHash);
95     cJSON_AddNumberToObject(jsonPstat, OIC_JSON_CM_NAME, (int)pstat->cm);
96     cJSON_AddNumberToObject(jsonPstat, OIC_JSON_TM_NAME, (int)pstat->tm);
97     cJSON_AddNumberToObject(jsonPstat, OIC_JSON_OM_NAME, (int)pstat->om);
98
99     cJSON_AddItemToObject(jsonPstat, OIC_JSON_SM_NAME, jsonSmArray = cJSON_CreateArray());
100     VERIFY_NON_NULL(TAG, jsonSmArray, INFO);
101     for (size_t i = 0; i < pstat->smLen; i++)
102     {
103         cJSON_AddItemToArray(jsonSmArray, cJSON_CreateNumber((int )pstat->sm[i]));
104     }
105     jsonStr = cJSON_Print(jsonRoot);
106
107 exit:
108     if (jsonRoot)
109     {
110         cJSON_Delete(jsonRoot);
111     }
112     return jsonStr;
113 }
114
115 OicSecPstat_t * JSONToPstatBin(const char * jsonStr)
116 {
117     if(NULL == jsonStr)
118     {
119         return NULL;
120     }
121
122     OCStackResult ret = OC_STACK_ERROR;
123     OicSecPstat_t *pstat = NULL;
124     cJSON *jsonPstat = NULL;
125     cJSON *jsonObj = NULL;
126
127     unsigned char base64Buff[sizeof(((OicUuid_t*) 0)->id)] = {};
128     uint32_t outLen = 0;
129     B64Result b64Ret = B64_OK;
130
131     cJSON *jsonRoot = cJSON_Parse(jsonStr);
132     VERIFY_NON_NULL(TAG, jsonRoot, INFO);
133
134     jsonPstat = cJSON_GetObjectItem(jsonRoot, OIC_JSON_PSTAT_NAME);
135     VERIFY_NON_NULL(TAG, jsonPstat, INFO);
136
137     pstat = (OicSecPstat_t*)OICCalloc(1, sizeof(OicSecPstat_t));
138     VERIFY_NON_NULL(TAG, pstat, INFO);
139     jsonObj = cJSON_GetObjectItem(jsonPstat, OIC_JSON_ISOP_NAME);
140     VERIFY_NON_NULL(TAG, jsonObj, ERROR);
141     VERIFY_SUCCESS(TAG, (cJSON_True == jsonObj->type || cJSON_False == jsonObj->type) , ERROR);
142     pstat->isOp = jsonObj->valueint;
143
144     jsonObj = cJSON_GetObjectItem(jsonPstat, OIC_JSON_DEVICE_ID_NAME);
145     VERIFY_NON_NULL(TAG, jsonObj, ERROR);
146     VERIFY_SUCCESS(TAG, cJSON_String == jsonObj->type, ERROR);
147     b64Ret = b64Decode(jsonObj->valuestring, strlen(jsonObj->valuestring), base64Buff,
148                 sizeof(base64Buff), &outLen);
149     VERIFY_SUCCESS(TAG, (b64Ret == B64_OK && outLen <= sizeof(pstat->deviceID.id)), ERROR);
150     memcpy(pstat->deviceID.id, base64Buff, outLen);
151
152     jsonObj = cJSON_GetObjectItem(jsonPstat, OIC_JSON_COMMIT_HASH_NAME);
153     VERIFY_NON_NULL(TAG, jsonObj, ERROR);
154     VERIFY_SUCCESS(TAG, cJSON_Number == jsonObj->type, ERROR);
155     pstat->commitHash  = jsonObj->valueint;
156
157     jsonObj = cJSON_GetObjectItem(jsonPstat, OIC_JSON_CM_NAME);
158     VERIFY_NON_NULL(TAG, jsonObj, ERROR);
159     VERIFY_SUCCESS(TAG, cJSON_Number == jsonObj->type, ERROR);
160     pstat->cm  = (OicSecDpm_t)jsonObj->valueint;
161
162     jsonObj = cJSON_GetObjectItem(jsonPstat, OIC_JSON_OM_NAME);
163     VERIFY_NON_NULL(TAG, jsonObj, ERROR);
164     VERIFY_SUCCESS(TAG, cJSON_Number == jsonObj->type, ERROR);
165     pstat->om  = (OicSecDpom_t)jsonObj->valueint;
166
167     jsonObj = cJSON_GetObjectItem(jsonPstat, OIC_JSON_SM_NAME);
168     VERIFY_NON_NULL(TAG, jsonObj, ERROR);
169     if (cJSON_Array == jsonObj->type)
170     {
171         pstat->smLen = cJSON_GetArraySize(jsonObj);
172         size_t idxx = 0;
173         VERIFY_SUCCESS(TAG, pstat->smLen != 0, ERROR);
174         pstat->sm = (OicSecDpom_t*)OICCalloc(pstat->smLen, sizeof(OicSecDpom_t));
175         VERIFY_NON_NULL(TAG, pstat->sm, ERROR);
176         do
177         {
178             cJSON *jsonSm = cJSON_GetArrayItem(jsonObj, idxx);
179             VERIFY_NON_NULL(TAG, jsonSm, ERROR);
180             pstat->sm[idxx] = (OicSecDpom_t)jsonSm->valueint;
181         }while ( ++idxx < pstat->smLen);
182     }
183     ret = OC_STACK_OK;
184
185 exit:
186     cJSON_Delete(jsonRoot);
187     if (OC_STACK_OK != ret)
188     {
189         OIC_LOG (ERROR, TAG, "JSONToPstatBin failed");
190         DeletePstatBinData(pstat);
191         pstat = NULL;
192     }
193     return pstat;
194 }
195
196 /**
197  * Function to update persistent storage
198  */
199 static bool UpdatePersistentStorage(OicSecPstat_t * pstat)
200 {
201     bool bRet = false;
202
203     if (pstat)
204     {
205         // Convert pstat data into JSON for update to persistent storage
206         char *jsonStr = BinToPstatJSON(pstat);
207         if (jsonStr)
208         {
209             cJSON *jsonPstat = cJSON_Parse(jsonStr);
210             OICFree(jsonStr);
211
212             if (jsonPstat &&
213                 (OC_STACK_OK == UpdateSVRDatabase(OIC_JSON_PSTAT_NAME, jsonPstat)))
214             {
215                 bRet = true;
216             }
217             cJSON_Delete(jsonPstat);
218         }
219     }
220
221     return bRet;
222 }
223
224
225 /**
226  * The entity handler determines how to process a GET request.
227  */
228 static OCEntityHandlerResult HandlePstatGetRequest (const OCEntityHandlerRequest * ehRequest)
229 {
230     OIC_LOG (INFO, TAG, "HandlePstatGetRequest  processing GET request");
231    // Convert ACL data into JSON for transmission
232     char* jsonStr = BinToPstatJSON(gPstat);
233
234     // A device should always have a default pstat. Therefore, jsonStr should never be NULL.
235     OCEntityHandlerResult ehRet = (jsonStr ? OC_EH_OK : OC_EH_ERROR);
236
237     // Send response payload to request originator
238     SendSRMResponse(ehRequest, ehRet, jsonStr);
239     OICFree(jsonStr);
240     return ehRet;
241 }
242
243 /**
244  * The entity handler determines how to process a POST request.
245  * Per the REST paradigm, POST can also be used to update representation of existing
246  * resource or create a new resource.
247  * For pstat, it updates only tm and om.
248  */
249 static OCEntityHandlerResult HandlePstatPutRequest(const OCEntityHandlerRequest *ehRequest)
250 {
251     OCEntityHandlerResult ehRet = OC_EH_ERROR;
252     cJSON *postJson = NULL;
253     OIC_LOG (INFO, TAG, "HandlePstatPutRequest  processing PUT request");
254
255     if (ehRequest->resource)
256     {
257         postJson = cJSON_Parse(((OCSecurityPayload*)ehRequest->payload)->securityData);
258         VERIFY_NON_NULL(TAG, postJson, INFO);
259         cJSON *jsonPstat = cJSON_GetObjectItem(postJson, OIC_JSON_PSTAT_NAME);
260         VERIFY_NON_NULL(TAG, jsonPstat, INFO);
261         cJSON *commitHashJson = cJSON_GetObjectItem(jsonPstat, OIC_JSON_COMMIT_HASH_NAME);
262         uint16_t commitHash = 0;
263         if (commitHashJson)
264         {
265             commitHash = commitHashJson->valueint;
266         }
267         cJSON *tmJson = cJSON_GetObjectItem(jsonPstat, OIC_JSON_TM_NAME);
268         if (tmJson && gPstat)
269         {
270             gPstat->tm = (OicSecDpm_t)tmJson->valueint;
271             if(0 == tmJson->valueint && gPstat->commitHash == commitHash)
272             {
273                 gPstat->isOp = true;
274                 gPstat->cm = NORMAL;
275                 OIC_LOG (INFO, TAG, "CommitHash is valid and isOp is TRUE");
276             }
277             else
278             {
279                 OIC_LOG (INFO, TAG, "CommitHash is not valid");
280             }
281         }
282         cJSON *omJson = cJSON_GetObjectItem(jsonPstat, OIC_JSON_OM_NAME);
283         if (omJson && gPstat)
284         {
285             /*
286              * Check if the operation mode is in the supported provisioning services
287              * operation mode list.
288              */
289             for(size_t i=0; i< gPstat->smLen; i++)
290             {
291                 if(gPstat->sm[i] == (unsigned int)omJson->valueint)
292                 {
293                     gPstat->om = (OicSecDpom_t)omJson->valueint;
294                     break;
295                 }
296             }
297         }
298         // Convert pstat data into JSON for update to persistent storage
299         if(UpdatePersistentStorage(gPstat))
300         {
301             ehRet = OC_EH_OK;
302         }
303     }
304  exit:
305     if(OC_EH_OK != ehRet)
306     {
307         /*
308           * If some error is occured while ownership transfer,
309           * ownership transfer related resource should be revert back to initial status.
310           */
311         RestoreDoxmToInitState();
312         RestorePstatToInitState();
313     }
314
315     //Send payload to request originator
316     if(OC_STACK_OK != SendSRMResponse(ehRequest, ehRet, NULL))
317     {
318         OIC_LOG (ERROR, TAG, "SendSRMResponse failed in HandlePstatPostRequest");
319     }
320     cJSON_Delete(postJson);
321     return ehRet;
322 }
323
324 /**
325  * This internal method is the entity handler for pstat resources.
326  */
327 OCEntityHandlerResult PstatEntityHandler(OCEntityHandlerFlag flag,
328         OCEntityHandlerRequest * ehRequest,
329         void *callbackParam)
330 {
331     (void)callbackParam;
332     OCEntityHandlerResult ehRet = OC_EH_ERROR;
333     // This method will handle REST request (GET/POST) for /oic/sec/pstat
334     if (flag & OC_REQUEST_FLAG)
335     {
336         OIC_LOG (INFO, TAG, "Flag includes OC_REQUEST_FLAG");
337         switch (ehRequest->method)
338         {
339             case OC_REST_GET:
340                 ehRet = HandlePstatGetRequest(ehRequest);
341                 break;
342             case OC_REST_PUT:
343                 ehRet = HandlePstatPutRequest(ehRequest);
344                 break;
345             default:
346                 ehRet = OC_EH_ERROR;
347                 SendSRMResponse(ehRequest, ehRet, NULL);
348                 break;
349         }
350     }
351     return ehRet;
352 }
353
354 /**
355  * This internal method is used to create '/oic/sec/pstat' resource.
356  */
357 OCStackResult CreatePstatResource()
358 {
359     OCStackResult ret;
360
361     ret = OCCreateResource(&gPstatHandle,
362                            OIC_RSRC_TYPE_SEC_PSTAT,
363                            OIC_MI_DEF,
364                            OIC_RSRC_PSTAT_URI,
365                            PstatEntityHandler,
366                            NULL,
367                            OC_RES_PROP_NONE);
368
369     if (ret != OC_STACK_OK)
370     {
371         OIC_LOG (FATAL, TAG, "Unable to instantiate pstat resource");
372         DeInitPstatResource();
373     }
374     return ret;
375 }
376
377 /**
378  * Post ACL hander update the commitHash during ACL provisioning.
379  */
380 void SetCommitHash(uint16_t commitHash)
381 {
382     gPstat->commitHash = commitHash;
383 }
384
385 /**
386  * Get the default value
387  * @retval  the gDefaultPstat pointer
388  */
389 static OicSecPstat_t* GetPstatDefault()
390 {
391     return &gDefaultPstat;
392 }
393
394 /**
395  * Initialize pstat resource by loading data from persistent storage.
396  *
397  * @retval  OC_STACK_OK for Success, otherwise some error value
398  */
399 OCStackResult InitPstatResource()
400 {
401     OCStackResult ret = OC_STACK_ERROR;
402
403     // Read Pstat resource from PS
404     char* jsonSVRDatabase = GetSVRDatabase();
405     if (jsonSVRDatabase)
406     {
407         // Convert JSON Pstat into binary format
408         gPstat = JSONToPstatBin(jsonSVRDatabase);
409     }
410     /*
411      * If SVR database in persistent storage got corrupted or
412      * is not available for some reason, a default pstat is created
413      * which allows user to initiate pstat provisioning again.
414      */
415     if(!jsonSVRDatabase || !gPstat)
416     {
417         gPstat = GetPstatDefault();
418     }
419
420     // Instantiate 'oic.sec.pstat'
421     ret = CreatePstatResource();
422
423     OICFree(jsonSVRDatabase);
424     return ret;
425 }
426
427 /**
428  * Perform cleanup for pstat resources.
429  *
430  * @retval  OC_STACK_OK for Success, otherwise some error value
431  */
432 OCStackResult DeInitPstatResource()
433 {
434     if(gPstat != &gDefaultPstat)
435     {
436         DeletePstatBinData(gPstat);
437         gPstat = NULL;
438     }
439     return OCDeleteResource(gPstatHandle);
440 }
441
442
443 /**
444  * Function to restore pstat resurce to initial status.
445  * This function will use in case of error while ownership transfer
446  */
447 void RestorePstatToInitState()
448 {
449     if(gPstat)
450     {
451         OIC_LOG(INFO, TAG, "PSTAT resource will revert back to initial status.");
452
453         gPstat->cm = NORMAL;
454         gPstat->tm = NORMAL;
455         gPstat->om = SINGLE_SERVICE_CLIENT_DRIVEN;
456         if(gPstat->sm && 0 < gPstat->smLen)
457         {
458             gPstat->sm[0] = SINGLE_SERVICE_CLIENT_DRIVEN;
459         }
460
461         if(!UpdatePersistentStorage(gPstat))
462         {
463             OIC_LOG(ERROR, TAG, "Failed to revert DOXM in persistent storage");
464         }
465     }
466 }