[IOT-2478] pstat.cm default in SRESET is incorrect
[iotivity.git] / resource / csdk / security / src / deviceonboardingstate.c
1 //******************************************************************
2 //
3 // Copyright 2017 Intel OpenSource Technology Center 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 "deviceonboardingstate.h"
22 #include "srmutility.h"
23 #include "octypes.h"
24 #include "logger.h"
25 #include "securevirtualresourcetypes.h"
26 #include "srmresourcestrings.h"
27 #include "aclresource.h"
28 #include "amaclresource.h"
29 #include "credresource.h"
30 #if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
31 #include "crlresource.h"
32 #endif /* (__WITH_DTLS__) || (__WITH_TLS__) */
33 #include "doxmresource.h"
34 #include "pstatresource.h"
35
36 #define TAG "OIC_SRM_DOS"
37
38 /**
39  * @return true if changing from oldState to newState is valid transition.
40  */
41 static bool IsValidStateTransition(OicSecDeviceOnboardingState_t oldState,
42     OicSecDeviceOnboardingState_t newState)
43 {
44     bool ret = false;
45
46     switch (newState)
47     {
48         case DOS_RESET:
49         ret = true;
50         break;
51
52         case DOS_RFNOP:
53         if (DOS_RFPRO == oldState)
54         {
55             ret = true;
56         }
57         break;
58
59         case DOS_RFOTM:
60         if (DOS_RESET == oldState)
61         {
62             ret = true;
63         }
64         break;
65
66         case DOS_RFPRO:
67         if (DOS_RFNOP == oldState
68             || DOS_RFOTM == oldState
69             || DOS_SRESET == oldState)
70         {
71             ret = true;
72         }
73         break;
74
75         case DOS_SRESET:
76         if (DOS_RFNOP == oldState
77             || DOS_RFPRO == oldState)
78         {
79             ret = true;
80         }
81         break;
82     }
83
84     OIC_LOG_V(INFO, TAG, "%s: returning %s.", __func__, ret?"true":"false");
85     return ret;
86 }
87
88 /**
89  * @return true if Device meets requirements to enter RFNOP DOS.
90  */
91 static bool IsReadyToEnterRFNOP()
92 {
93     bool ret = false;
94     bool tempBool = false;
95     OicUuid_t tempUuid = {.id={0}};
96
97     // Note: pstat.dos.p asserted by DoStateChange(), so not checked here.
98
99     // Verify doxm.owned == TRUE.
100     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmIsOwned(&tempBool), ERROR);
101     VERIFY_TRUE_OR_EXIT(TAG, tempBool, WARNING);
102
103     // Verify doxm.devowneruuid != nil UUID.
104     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmDevOwnerId(&tempUuid), ERROR);
105     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
106
107     // Verify doxm.deviceuuid != nil UUID.
108     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmDeviceID(&tempUuid), ERROR);
109     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
110
111     // Verify oxmsel was the actual OTM used (no-op: CTT will verify this during
112     // certification testing, as it requires OBT cooperation to verify).
113
114     // Verify pstat.isop == false (Server sets isop on entry)
115     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetPstatIsop(&tempBool), ERROR);
116     VERIFY_TRUE_OR_EXIT(TAG, !tempBool, WARNING);
117
118     // Verify implemented SVRs with rowneruuid Property have non-Nil rowneruuid
119     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetAclRownerId(&tempUuid), ERROR);
120     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
121
122     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetCredRownerId(&tempUuid), ERROR);
123     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
124
125     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmRownerId(&tempUuid), ERROR);
126     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
127
128     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetPstatRownerId(&tempUuid), ERROR);
129     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
130
131     // Verify each rowneruuid, devowneruuid has a corresponding /cred entry
132     // TODO [IOT-2023]
133
134     ret = true;
135
136 exit:
137     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
138     return ret;
139 }
140
141 /**
142  * @return true if Device meets requirements to enter RFOTM DOS.
143  */
144 static bool IsReadyToEnterRFOTM()
145 {
146     bool ret = false;
147     bool tempBool = false;
148     OicUuid_t tempUuid = {.id={0}};
149
150     // Note: pstat.dos.p asserted by DoStateChange(), so not checked here.
151
152     // Verify doxm.owned == FALSE.
153     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmIsOwned(&tempBool), ERROR);
154     VERIFY_TRUE_OR_EXIT(TAG, !tempBool, WARNING);
155
156     // Verify doxm.devowneruuid == nil UUID.
157     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmDevOwnerId(&tempUuid), ERROR);
158     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
159
160     // Check and log whether doxm.deviceuuid == nil UUID ("may" reqt not "shall")
161     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmDeviceID(&tempUuid), ERROR);
162     if (!IsNilUuid(&tempUuid))
163     {
164         OIC_LOG_V(INFO, TAG, "%s: doxm.deviceuuid != Nil UUID... allowed but noted.",
165             __func__);
166     }
167
168     ret = true;
169
170 exit:
171     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
172     return ret;
173 }
174
175 /**
176  * @return true if Device meets requirements to enter RFPRO DOS.
177  */
178 static bool IsReadyToEnterRFPRO()
179 {
180     bool ret = false;
181     bool tempBool = false;
182     OicUuid_t tempUuid = {.id={0}};
183
184     // Note: pstat.dos.p asserted by DoStateChange(), so not checked here.
185
186     // Verify doxm.owned == TRUE.
187     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmIsOwned(&tempBool), ERROR);
188     VERIFY_TRUE_OR_EXIT(TAG, tempBool, WARNING);
189
190     // Verify doxm.devowneruuid != nil UUID.
191     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmDevOwnerId(&tempUuid), ERROR);
192     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
193
194     // Verify doxm.deviceuuid != nil UUID.
195     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmDeviceID(&tempUuid), ERROR);
196     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
197
198     // doxm.sct and doxm.oxmsel retain previous values (checked by CTT)
199
200     // Verify implemented SVRs with rowneruuid Property have non-Nil rowneruuid
201     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetAclRownerId(&tempUuid), ERROR);
202     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
203
204     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetCredRownerId(&tempUuid), ERROR);
205     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
206
207     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmRownerId(&tempUuid), ERROR);
208     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
209
210     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetPstatRownerId(&tempUuid), ERROR);
211     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
212
213     // Verify each rowneruuid, devowneruuid has a corresponding /cred entry
214     // TODO [IOT-2023]
215
216     ret = true;
217
218 exit:
219     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
220     return ret;
221 }
222
223 /**
224  * @return true if Device meets requirements to set pstat.dos.s = SRESET.
225  */
226 static bool IsReadyToEnterSRESET()
227 {
228     bool ret = false;
229     bool tempBool = false;
230     OicUuid_t tempUuid = {.id={0}};
231
232     // Note: pstat.dos.p set by DoStateChange(), so not checked here.
233
234     // TODO [IOT-2023]: sanity check SVRs (optional)
235
236     // Verify doxm.owned == TRUE.
237     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmIsOwned(&tempBool), ERROR);
238     VERIFY_TRUE_OR_EXIT(TAG, tempBool, WARNING);
239
240     // Verify doxm.devowneruuid != nil UUID.
241     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmDevOwnerId(&tempUuid), ERROR);
242     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
243
244     // Verify doxm.deviceuuid != nil UUID.
245     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetDoxmDeviceID(&tempUuid), ERROR);
246     VERIFY_TRUE_OR_EXIT(TAG, !IsNilUuid(&tempUuid), WARNING);
247
248     // doxm.sct and doxm.oxmsel retain previous values (checked by CTT)
249
250     ret = true;
251
252 exit:
253     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
254     return ret;
255 }
256
257 /**
258  * Generic ops performed on entering most states, coalesced to avoid repeat code.
259  */
260 static bool EnterStateGeneric(bool isop,
261                               bool cmReset,
262                               bool cmTakeOwner,
263                               bool tmReset,
264                               bool tmTakeOwner,
265                               OicSecDeviceOnboardingState_t state)
266 {
267     bool ret = false;
268     OicSecDpm_t cm = 0;
269     OicSecDpm_t tm = 0;
270
271     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetPstatCm(&cm), ERROR);
272     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetPstatTm(&tm), ERROR);
273
274     // Set pstat.isop
275     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetPstatIsop(isop), ERROR);
276
277     // Set pstat.cm
278     if (cmReset)
279     {
280         cm |= RESET; // set RESET bit
281     }
282     else
283     {
284         cm &= ~RESET; // clear RESET bit
285     }
286     if (cmTakeOwner)
287     {
288         cm |= TAKE_OWNER; // set TAKE_OWNER bit
289     }
290     else
291     {
292         cm &= ~TAKE_OWNER; // clear TAKE_OWNER bit
293     }
294     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetPstatCm(cm), ERROR);
295
296     // Set pstat.tm
297     if (tmReset)
298     {
299         tm |= RESET; // set RESET bit
300     }
301     else
302     {
303         tm &= ~RESET; // clear RESET bit
304     }
305     if (tmTakeOwner)
306     {
307         tm |= TAKE_OWNER; // set TAKE_OWNER bit
308     }
309     else
310     {
311         tm &= ~TAKE_OWNER; // clear TAKE_OWNER bit
312     }
313     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetPstatTm(tm), ERROR);
314
315     // Set pstat.dos.s
316     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetPstatDosS(state), ERROR);
317
318     ret = true;
319
320 exit:
321     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
322     return ret;
323 }
324
325 /**
326  * Enter RFNOP state and set all Server-controlled SVR Property values.
327  */
328 static bool EnterRFNOP()
329 {
330     bool ret = false;
331
332     // Set pstat.isop = TRUE
333     // Unset pstat.cm RESET and TAKE_OWNER bits
334     // Unset pstat.tm RESET and TAKE_OWNER bits
335     // Set pstat.dos to RFNOP
336     ret = EnterStateGeneric(true, false, false, false, false, DOS_RFNOP);
337
338     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
339     return ret;
340 }
341
342 /**
343  * Enter RFOTM state and set all Server-controlled SVR Property values.
344  */
345 static bool EnterRFOTM()
346 {
347     bool ret = false;
348
349     // Set pstat.isop = FALSE
350     // Unset pstat.cm RESET bit, and set TAKE_OWNER bit
351     // Unset pstat.tm RESET and TAKE_OWNER bits
352     // Set pstat.dos to RFOTM
353     ret = EnterStateGeneric(false, false, true, false, false, DOS_RFOTM);
354
355     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
356     return ret;
357 }
358
359 /**
360  * Enter RFPRO state and set all Server-controlled SVR Property values.
361  */
362 static bool EnterRFPRO()
363 {
364     bool ret = false;
365
366     // Set pstat.isop = FALSE
367     // Unset pstat.cm RESET and TAKE_OWNER bits
368     // Unset pstat.tm RESET and TAKE_OWNER bits
369     // Set pstat.dos to RFPRO
370     ret = EnterStateGeneric(false, false, false, false, false, DOS_RFPRO);
371
372     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
373     return ret;
374 }
375
376 /**
377  * Enter RESET state and set all Server-controlled SVR Property values.
378  */
379 static bool EnterRESET()
380 {
381     bool ret = false;
382
383     // Restore Mfr Defaults
384     // TODO [IOT-2023]: we need OSWG Security TG to decide on how "mfr defaults"
385     // should really work.  Hard coded SVRs?  Backup .dat file?  Hard coded
386     // policy without SVRs?  IMO this is *highly* platform and mfr process
387     // dependent and probably isn't worth investing the time to create an
388     // IoTivity "backup/restore" capability.  Instead the need to do so should
389     // be documented in the device vendor certification paperwork, per BZ 1383.
390
391     // Set doxm.deviceuuid = Mfr Default (handled above)
392     // Set doxm.sct = Mfr Default ("")
393     // Set doxm.oxmsel = Mfr Default ("")
394     // Set pstat.om = Mfr Default ("")
395     // Set pstat.sm = Mfr Default ("")
396     // Assert acl2, amacl, sacl, cred arrays = mfr defaults ("")
397
398     // Set doxm.owned = FALSE
399     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetDoxmIsOwned(false), ERROR);
400
401     // Set doxm.devowneruuid = Nil UUID
402     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetDoxmDevOwnerId(&THE_NIL_UUID), ERROR);
403
404     // Set acl, doxm, cred and pstat rowneruuids = Nil UUID
405     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetAclRownerId(&THE_NIL_UUID), ERROR);
406     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetCredRownerId(&THE_NIL_UUID), ERROR);
407     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetDoxmRownerId(&THE_NIL_UUID), ERROR);
408     VERIFY_SUCCESS(TAG, OC_STACK_OK == SetPstatRownerId(&THE_NIL_UUID), ERROR);
409
410     // Set pstat.isop = FALSE
411     // Set pstat.cm RESET and unset TAKE_OWNER
412     // Unset pstat.tm and set TAKE_OWNER
413     // Set pstat.dos.s to RESET
414     VERIFY_SUCCESS(TAG,
415         EnterStateGeneric(false, true, false, false, true, DOS_RESET),
416         ERROR);
417
418 exit:
419     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
420     return ret;
421 }
422
423 /**
424  * Enter SRESET state and set all Server-controlled SVR Property values.
425  */
426 static bool EnterSRESET()
427 {
428     bool ret = false;
429
430     // Set pstat.isop = FALSE
431     // Set pstat.cm RESET and unset TAKE_OWNER
432     // Unset pstat.tm and unset TAKE_OWNER
433     // Set pstat.dos.s to RESET
434     VERIFY_SUCCESS(TAG,
435         EnterStateGeneric(false, true, false, false, false, DOS_SRESET),
436         ERROR);
437
438     ret = true;
439
440 exit:
441     OIC_LOG_V(DEBUG, TAG, "%s: returning %s.", __func__, ret?"true":"false");
442     return ret;
443 }
444
445 /**
446  * Set pstat.dos.p (pending) to true, then verify device is ready to perform
447  * the state change.  If so, perform the state change.  Finally, set
448  * pstat.dos.p (pending) to false.
449  * @return  OC_STACK_OK if successful change to newState
450  *          OC_STACK_FORBIDDEN_REQ if state change preconditions not met
451  *          OC_STACK_INTERNAL_SERVER_ERROR if SVRs left in potentially unstable state
452  */
453 static OCStackResult DoStateChange(OicSecDeviceOnboardingState_t newState)
454 {
455     OCStackResult ret = OC_STACK_INTERNAL_SERVER_ERROR;
456
457     switch (newState)
458     {
459         case DOS_RESET:
460         // No preconditions other than setting dos.p = true, which is done above
461         if (EnterRESET())
462         {
463             ret = OC_STACK_OK;
464         }
465         else
466         {
467             ret = OC_STACK_INTERNAL_SERVER_ERROR;
468         }
469         break;
470
471         case DOS_RFNOP:
472         if (IsReadyToEnterRFNOP())
473         {
474             if (EnterRFNOP())
475             {
476                 ret = OC_STACK_OK;
477             }
478             else
479             {
480                 ret = OC_STACK_INTERNAL_SERVER_ERROR;
481             }
482         }
483         else
484         {
485             ret = OC_STACK_FORBIDDEN_REQ;
486         }
487         break;
488
489         case DOS_RFOTM:
490         if (IsReadyToEnterRFOTM())
491         {
492             if (EnterRFOTM())
493             {
494                 ret = OC_STACK_OK;
495             }
496             else
497             {
498                 ret = OC_STACK_INTERNAL_SERVER_ERROR;
499             }
500         }
501         else
502         {
503             ret = OC_STACK_FORBIDDEN_REQ;
504         }
505         break;
506
507         case DOS_RFPRO:
508         if (IsReadyToEnterRFPRO())
509         {
510             if (EnterRFPRO())
511             {
512                 ret = OC_STACK_OK;
513             }
514             else
515             {
516                 ret = OC_STACK_INTERNAL_SERVER_ERROR;
517             }
518         }
519         else
520         {
521             ret = OC_STACK_FORBIDDEN_REQ;
522         }
523         break;
524
525         case DOS_SRESET:
526         if (IsReadyToEnterSRESET())
527         {
528             if (EnterSRESET())
529             {
530                 ret = OC_STACK_OK;
531             }
532             else
533             {
534                 ret = OC_STACK_INTERNAL_SERVER_ERROR;
535             }
536         }
537         else
538         {
539             ret = OC_STACK_FORBIDDEN_REQ;
540         }
541         break;
542     }
543
544     OIC_LOG_V(DEBUG, TAG, "%s: returning %d.", __func__, ret);
545     return ret;
546 }
547
548 OCStackResult GetDos(OicSecDostype_t *dos)
549 {
550     if (dos)
551     {
552         VERIFY_SUCCESS(TAG, OC_STACK_OK == GetPstatDosS(&(dos->state)), ERROR);
553         VERIFY_SUCCESS(TAG, OC_STACK_OK == GetPstatDosP(&(dos->pending)), ERROR);
554         return OC_STACK_OK;
555     }
556 exit:
557     return OC_STACK_ERROR;
558 }
559
560 OCStackResult SetDosState(const OicSecDeviceOnboardingState_t desiredState)
561 {
562     OIC_LOG_V(INFO, TAG, "%s called for state %d.", __func__, desiredState);
563
564     OCStackResult ret = OC_STACK_ERROR;
565     bool pending = false;
566
567     VERIFY_SUCCESS(TAG, OC_STACK_OK == GetPstatDosP(&pending), ERROR);
568
569     if (!pending)
570     {
571
572         VERIFY_SUCCESS(TAG, OC_STACK_OK == SetPstatDosP(true), ERROR);
573
574         OicSecDeviceOnboardingState_t oldState = DOS_RESET;
575         VERIFY_SUCCESS(TAG, OC_STACK_OK == GetPstatDosS(&oldState), ERROR);
576         if (IsValidStateTransition(oldState, desiredState))
577         {
578             OCStackResult stateChangeResult = DoStateChange(desiredState);
579             switch (stateChangeResult)
580             {
581                 case OC_STACK_OK:
582                 OIC_LOG_V(INFO, TAG, "%s: DOS state changed SUCCESSFULLY from %d to %d.",
583                     __func__, oldState, desiredState);
584                 ret = OC_STACK_OK;
585                 break;
586
587                 case OC_STACK_FORBIDDEN_REQ:
588                 OIC_LOG_V(WARNING, TAG, "%s: DOS state change change from %d to %d NOT ALLOWED.",
589                     __func__, oldState, desiredState);
590                 ret = OC_STACK_FORBIDDEN_REQ;
591                 break;
592
593                 case OC_STACK_INTERNAL_SERVER_ERROR:
594                 default:
595                 OIC_LOG_V(ERROR, TAG, "%s: DOS state change change from %d to %d FAILED."
596                     " Internal error - SVRs may be in bad state.",
597                     __func__, oldState, desiredState);
598                 ret = OC_STACK_INTERNAL_SERVER_ERROR;
599                 break;
600             }
601         }
602         else
603         {
604             OIC_LOG_V(INFO, TAG, "%s: Invalid transition; cannot go from %d to %d.", \
605                 __func__, oldState, desiredState);
606             ret = OC_STACK_FORBIDDEN_REQ;
607         }
608
609         VERIFY_SUCCESS(TAG, OC_STACK_OK == SetPstatDosP(false), ERROR);
610
611     }
612     else
613     {
614         OIC_LOG_V(WARNING, TAG, "%s: cannot set pstat->dos.s, change \
615             already pending.", __func__);
616         ret = OC_STACK_FORBIDDEN_REQ;
617     }
618
619     // TODO [IOT-2023] implement RESET->RFOTM change once supported by prov tool
620
621     // TODO [IOT-2023] if OC_STACK_OK, update all SVRs in Persistent Storage?
622
623     return ret;
624
625 exit:
626     return OC_STACK_INTERNAL_SERVER_ERROR;
627 }