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