Security code comments which are doxygen compliant
[platform/upstream/iotivity.git] / resource / csdk / security / src / policyengine.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 #include <string.h>
21
22 #include "oic_malloc.h"
23 #include "policyengine.h"
24 #include "amsmgr.h"
25 #include "resourcemanager.h"
26 #include "securevirtualresourcetypes.h"
27 #include "srmresourcestrings.h"
28 #include "logger.h"
29 #include "aclresource.h"
30 #include "srmutility.h"
31 #include "doxmresource.h"
32 #include "iotvticalendar.h"
33
34 #define TAG "SRM-PE"
35
36 uint16_t GetPermissionFromCAMethod_t(const CAMethod_t method)
37 {
38     uint16_t perm = 0;
39     switch (method)
40     {
41         case CA_GET:
42             perm = (uint16_t)PERMISSION_READ;
43             break;
44         case CA_POST: // For now we treat all PUT & POST as Write
45         case CA_PUT:  // because we don't know if resource exists yet.
46             perm = (uint16_t)PERMISSION_WRITE;
47             break;
48         case CA_DELETE:
49             perm = (uint16_t)PERMISSION_DELETE;
50             break;
51         default: // if not recognized, must assume requesting full control
52             perm = (uint16_t)PERMISSION_FULL_CONTROL;
53             break;
54     }
55     return perm;
56 }
57
58 /**
59  * Compares two OicUuid_t structs.
60  *
61  * @return true if the two OicUuid_t structs are equal, else false.
62  */
63 static bool UuidCmp(OicUuid_t *firstId, OicUuid_t *secondId)
64 {
65     // TODO use VERIFY macros to check for null when they are merged.
66     if(NULL == firstId || NULL == secondId)
67     {
68         return false;
69     }
70     for(int i = 0; i < UUID_LENGTH; i++)
71     {
72         if(firstId->id[i] != secondId->id[i])
73         {
74             return false;
75         }
76     }
77     return true;
78 }
79
80 void SetPolicyEngineState(PEContext_t *context, const PEState_t state)
81 {
82     if (NULL == context)
83     {
84         return;
85     }
86
87     // Clear stateful context variables.
88     memset(&context->subject, 0, sizeof(context->subject));
89     memset(&context->resource, 0, sizeof(context->resource));
90     context->permission = 0x0;
91     context->matchingAclFound = false;
92     context->amsProcessing = false;
93     context->retVal = ACCESS_DENIED_POLICY_ENGINE_ERROR;
94
95     FreeCARequestInfo(context->amsMgrContext->requestInfo);
96     OICFree(context->amsMgrContext->endpoint);
97     memset(context->amsMgrContext, 0, sizeof(AmsMgrContext_t));
98
99     // Set state.
100     context->state = state;
101 }
102
103 /**
104  * Compare the request's subject to DevOwner.
105  *
106  * @return true if context->subjectId == GetDoxmDevOwner(), else false.
107  */
108 static bool IsRequestFromDevOwner(PEContext_t *context)
109 {
110     bool retVal = false;
111     OicUuid_t owner;
112
113     if(NULL == context)
114     {
115         return OC_STACK_ERROR;
116     }
117
118     if(OC_STACK_OK == GetDoxmDevOwnerId(&owner))
119     {
120         retVal = UuidCmp(&context->subject, &owner);
121     }
122
123     return retVal;
124 }
125
126 inline static bool IsRequestSubjectEmpty(PEContext_t *context)
127 {
128     OicUuid_t emptySubject = {.id={}};
129
130     if(NULL == context)
131     {
132         return false;
133     }
134
135     return (memcmp(&context->subject, &emptySubject, sizeof(OicUuid_t)) == 0) ?
136             true : false;
137 }
138
139 /**
140  * Bitwise check to see if 'permission' contains 'request'.
141  *
142  * @param permission is the allowed CRUDN permission.
143  * @param request is the CRUDN permission being requested.
144  *
145  * @return true if 'permission' bits include all 'request' bits.
146  */
147 static inline bool IsPermissionAllowingRequest(const uint16_t permission,
148     const uint16_t request)
149 {
150     if (request == (request & permission))
151     {
152         return true;
153     }
154     else
155     {
156         return false;
157     }
158 }
159
160 /**
161  * Compare the passed subject to the wildcard (aka anonymous) subjectId.
162  *
163  * @return true if 'subject' is the wildcard, false if it is not.
164  */
165 static inline bool IsWildCardSubject(OicUuid_t *subject)
166 {
167     if(NULL == subject)
168     {
169         return false;
170     }
171
172     // Because always comparing to string literal, use strcmp()
173     if(0 == memcmp(subject, &WILDCARD_SUBJECT_ID, sizeof(OicUuid_t)))
174     {
175         return true;
176     }
177     else
178     {
179         return false;
180     }
181 }
182
183 /**
184  * Copy the subject, resource and permission into the context fields.
185  */
186 static void CopyParamsToContext(PEContext_t     *context,
187                                 const OicUuid_t *subjectId,
188                                 const char      *resource,
189                                 const uint16_t  requestedPermission)
190 {
191     size_t length = 0;
192
193     if (NULL == context || NULL == subjectId || NULL == resource)
194     {
195         return;
196     }
197
198     memcpy(&context->subject, subjectId, sizeof(OicUuid_t));
199
200     // Copy the resource string into context.
201     length = strlen(resource) + 1;
202     if (0 < length)
203     {
204         strncpy(context->resource, resource, length);
205         context->resource[length - 1] = '\0';
206     }
207
208     // Assign the permission field.
209     context->permission = requestedPermission;
210 }
211
212 /**
213  * Check whether 'resource' is getting accessed within the valid time period.
214  *
215  * @param acl is the ACL to check.
216  *
217  * @return true if access is within valid time period or if the period or recurrence is not present.
218  * false if period and recurrence present and the access is not within valid time period.
219  */
220 static bool IsAccessWithinValidTime(const OicSecAcl_t *acl)
221 {
222 #ifndef WITH_ARDUINO //Period & Recurrence not supported on Arduino due
223                      //lack of absolute time
224     if (NULL== acl || NULL == acl->periods || 0 == acl->prdRecrLen)
225     {
226         return true;
227     }
228
229     //periods & recurrences rules are paired.
230     if (NULL == acl->recurrences)
231     {
232         return false;
233     }
234
235     for (size_t i = 0; i < acl->prdRecrLen; i++)
236     {
237         if (IOTVTICAL_VALID_ACCESS ==  IsRequestWithinValidTime(acl->periods[i],
238             acl->recurrences[i]))
239         {
240             OIC_LOG(INFO, TAG, "Access request is in allowed time period");
241             return true;
242         }
243     }
244     OIC_LOG(ERROR, TAG, "Access request is in invalid time period");
245     return false;
246
247 #else
248     return true;
249 #endif
250 }
251
252 /**
253  * Check whether 'resource' is in the passed ACL.
254  *
255  * @param resource is the resource being searched.
256  * @param acl is the ACL to check.
257  *
258  * @return true if 'resource' found, otherwise false.
259  */
260  static bool IsResourceInAcl(const char *resource, const OicSecAcl_t *acl)
261 {
262     if (NULL== acl || NULL == resource)
263     {
264         return false;
265     }
266
267      for (size_t n = 0; n < acl->resourcesLen; n++)
268      {
269          if (0 == strcmp(resource, acl->resources[n]) || // TODO null terms?
270              0 == strcmp(WILDCARD_RESOURCE_URI, acl->resources[n]))
271          {
272              return true;
273          }
274     }
275     return false;
276 }
277
278
279 /**
280  * Find ACLs containing context->subject.
281  * Search each ACL for requested resource.
282  * If resource found, check for context->permission and period validity.
283  * If the ACL is not found locally and AMACL for the resource is found
284  * then sends the request to AMS service for the ACL.
285  * Set context->retVal to result from first ACL found which contains
286  * correct subject AND resource.
287  */
288 static void ProcessAccessRequest(PEContext_t *context)
289 {
290     OIC_LOG(DEBUG, TAG, "Entering ProcessAccessRequest()");
291     if (NULL != context)
292     {
293         const OicSecAcl_t *currentAcl = NULL;
294         OicSecAcl_t *savePtr = NULL;
295
296         // Start out assuming subject not found.
297         context->retVal = ACCESS_DENIED_SUBJECT_NOT_FOUND;
298
299         // Loop through all ACLs with a matching Subject searching for the right
300         // ACL for this request.
301         do
302         {
303             OIC_LOG_V(DEBUG, TAG, "%s: getting ACL..." ,__func__);
304             currentAcl = GetACLResourceData(&context->subject, &savePtr);
305
306             if (NULL != currentAcl)
307             {
308                 // Found the subject, so how about resource?
309                 OIC_LOG_V(DEBUG, TAG, "%s:found ACL matching subject" ,__func__);
310
311                 // Subject was found, so err changes to Rsrc not found for now.
312                 context->retVal = ACCESS_DENIED_RESOURCE_NOT_FOUND;
313                 OIC_LOG_V(DEBUG, TAG, "%s:Searching for resource..." ,__func__);
314                 if (IsResourceInAcl(context->resource, currentAcl))
315                 {
316                     OIC_LOG_V(INFO, TAG, "%s:found matching resource in ACL" ,__func__);
317                     context->matchingAclFound = true;
318
319                     // Found the resource, so it's down to valid period & permission.
320                     context->retVal = ACCESS_DENIED_INVALID_PERIOD;
321                     if (IsAccessWithinValidTime(currentAcl))
322                     {
323                         context->retVal = ACCESS_DENIED_INSUFFICIENT_PERMISSION;
324                         if (IsPermissionAllowingRequest(currentAcl->permission, context->permission))
325                         {
326                             context->retVal = ACCESS_GRANTED;
327                         }
328                     }
329                 }
330             }
331             else
332             {
333                 OIC_LOG_V(INFO, TAG, "%s:no ACL found matching subject for resource %s",__func__, context->resource);
334             }
335         } while ((NULL != currentAcl) && (false == context->matchingAclFound));
336
337         if (IsAccessGranted(context->retVal))
338         {
339             OIC_LOG_V(INFO, TAG, "%s:Leaving ProcessAccessRequest(ACCESS_GRANTED)", __func__);
340         }
341         else
342         {
343             OIC_LOG_V(INFO, TAG, "%s:Leaving ProcessAccessRequest(ACCESS_DENIED)", __func__);
344         }
345     }
346     else
347     {
348         OIC_LOG_V(ERROR, TAG, "%s:Leaving ProcessAccessRequest(context is NULL)", __func__);
349     }
350 }
351
352 SRMAccessResponse_t CheckPermission(PEContext_t     *context,
353                                     const OicUuid_t *subjectId,
354                                     const char      *resource,
355                                     const uint16_t  requestedPermission)
356 {
357     SRMAccessResponse_t retVal = ACCESS_DENIED_POLICY_ENGINE_ERROR;
358
359     VERIFY_NON_NULL(TAG, context, ERROR);
360     VERIFY_NON_NULL(TAG, subjectId, ERROR);
361     VERIFY_NON_NULL(TAG, resource, ERROR);
362
363     // Each state machine context can only be processing one request at a time.
364     // Therefore if the context is not in AWAITING_REQUEST or AWAITING_AMS_RESPONSE
365     // state, return error. Otherwise, change to BUSY state and begin processing request.
366     if (AWAITING_REQUEST == context->state || AWAITING_AMS_RESPONSE == context->state)
367     {
368         if (AWAITING_REQUEST == context->state)
369         {
370             SetPolicyEngineState(context, BUSY);
371             CopyParamsToContext(context, subjectId, resource, requestedPermission);
372         }
373
374         // Before doing any processing, check if request coming
375         // from DevOwner and if so, always GRANT.
376         if (IsRequestFromDevOwner(context))
377         {
378             context->retVal = ACCESS_GRANTED;
379         }
380         else
381         {
382             OicUuid_t saveSubject = {.id={}};
383             bool isSubEmpty = IsRequestSubjectEmpty(context);
384
385             ProcessAccessRequest(context);
386
387             // If matching ACL not found, and subject != wildcard, try wildcard.
388             if ((false == context->matchingAclFound) && \
389               (false == IsWildCardSubject(&context->subject)))
390             {
391                 //Saving subject for Amacl check
392                 memcpy(&saveSubject, &context->subject,sizeof(OicUuid_t));
393
394                 //Setting context subject to WILDCARD_SUBJECT_ID
395                 //TODO: change ProcessAccessRequest method signature to
396                 //ProcessAccessRequest(context, subject) so that context
397                 //subject is not tempered.
398                 memset(&context->subject, 0, sizeof(context->subject));
399                 memcpy(&context->subject, &WILDCARD_SUBJECT_ID,sizeof(OicUuid_t));
400                 ProcessAccessRequest(context); // TODO anonymous subj can result
401                                                // in confusing err code return.
402             }
403
404             //No local ACE found for the request so checking Amacl resource
405             if (ACCESS_GRANTED != context->retVal)
406             {
407                 //If subject is not empty then restore the original subject
408                 //else keep the subject to WILDCARD_SUBJECT_ID
409                 if(!isSubEmpty)
410                 {
411                     memcpy(&context->subject, &saveSubject, sizeof(OicUuid_t));
412                 }
413
414                 //FoundAmaclForRequest method checks for Amacl and fills up
415                 //context->amsMgrContext->amsDeviceId with the AMS deviceId
416                 //if Amacl was found for the requested resource.
417                 if(FoundAmaclForRequest(context))
418                 {
419                     ProcessAMSRequest(context);
420                 }
421             }
422         }
423     }
424     else
425     {
426         context->retVal = ACCESS_DENIED_POLICY_ENGINE_ERROR;
427     }
428
429     // Capture retVal before resetting state for next request.
430     retVal = context->retVal;
431
432    if (!context->amsProcessing)
433     {
434         OIC_LOG(INFO, TAG, "Resetting PE context and PE State to AWAITING_REQUEST");
435         SetPolicyEngineState(context, AWAITING_REQUEST);
436     }
437
438 exit:
439     return retVal;
440 }
441
442 OCStackResult InitPolicyEngine(PEContext_t *context)
443 {
444     if(NULL == context)
445     {
446         return OC_STACK_ERROR;
447     }
448
449     context->amsMgrContext = (AmsMgrContext_t *)OICCalloc(1, sizeof(AmsMgrContext_t));
450     if(NULL == context->amsMgrContext)
451     {
452         return OC_STACK_ERROR;
453     }
454
455     SetPolicyEngineState(context, AWAITING_REQUEST);
456     return OC_STACK_OK;
457 }
458
459 void DeInitPolicyEngine(PEContext_t *context)
460 {
461     if(NULL != context)
462     {
463         SetPolicyEngineState(context, STOPPED);
464         OICFree(context->amsMgrContext);
465     }
466     return;
467 }