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