dc7485dc170a420e9e97ff96e2fdc5bf58b5eddb
[platform/upstream/iotivity.git] / resource / csdk / security / provisioning / ck_manager / sample / provisioningclient.c
1 /******************************************************************
2 *
3 * Copyright 2015 Samsung Electronics 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 <stdio.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include "logger.h"
26 #include "oic_malloc.h"
27 #include "utlist.h"
28 #include "ocprovisioningmanager.h"
29 #include "secureresourceprovider.h"
30 #include "oxmjustworks.h"
31 #include "oic_string.h"
32 #include "securevirtualresourcetypes.h"
33 #include "cacommon.h"
34 #include "ck_manager.h"
35 #include "ckm_info.h"
36 #include "crlresource.h"
37
38 #define MAX_URI_LENGTH (64)
39 #define MAX_PERMISSION_LENGTH (5)
40 #define MAX_ACL_LENGTH 100
41 #define CREATE (1)
42 #define READ (2)
43 #define UPDATE (4)
44 #define DELETE (8)
45 #define NOTIFY (16)
46 #define DASH '-'
47 #define PREDEFINED_TIMEOUT (10)
48 #define MAX_OWNED_DEVICE (10)
49 #define DATE_LENGTH      (14)
50 #define TAG  "provisioningclient"
51
52 static OicSecAcl_t        *gAcl = NULL;
53 static OicSecCrl_t        *gCrl = NULL;
54 static char PROV_TOOL_DB_FILE[] = "oic_svr_db_pt.dat";
55 static const char* PRVN_DB_FILE_NAME = "oic_prvn_mng.db";
56 static int gOwnershipState = 0;
57
58 typedef enum
59 {
60     ownershipDone = 1 << 1,
61     finalizeDone = 1 << 2,
62     provisionAclDone = 1 << 3,
63     provisionCert1Done = 1 << 4,
64     provisionCert2Done = 1 << 5,
65     provisionCrlDone = 1 << 6
66 } StateManager;
67
68
69 /**
70  * Perform cleanup for ACL
71  * @param[in]    ACL
72  */
73 static void deleteACL(OicSecAcl_t *acl)
74 {
75     if (acl && MAX_ACL_LENGTH > acl->resourcesLen)
76     {
77         /* Clean Resources */
78         for (size_t i = 0; i < (acl)->resourcesLen; i++)
79         {
80             OICFree((acl)->resources[i]);
81         }
82         OICFree((acl)->resources);
83
84         /* Clean ACL node itself */
85         OICFree((acl));
86
87         acl = NULL;
88     }
89 }
90
91 void deleteCrl(OicSecCrl_t *crl)
92 {
93     if (crl)
94     {
95         //Clean ThisUpdate
96         OICFree(crl->ThisUpdate.data);
97
98         //clean CrlData
99         OICFree(crl->CrlData.data);
100
101         //Clean crl itself
102         OICFree(crl);
103     }
104 }
105
106 /**
107  * Calculate ACL permission from string to bit
108  *
109  * @param[in] temp_psm    Input data of ACL permission string
110  * @param[in,out] pms    The pointer of ACL permission value
111  * @return  0 on success otherwise -1.
112  */
113 static int CalculateAclPermission(const char *temp_pms, uint16_t *pms)
114 {
115     int i = 0;
116
117     if (NULL == temp_pms || NULL == pms)
118     {
119         return -1;
120     }
121     *pms = 0;
122     while (temp_pms[i] != '\0')
123     {
124         switch (temp_pms[i])
125         {
126             case 'C':
127                 {
128                     *pms += CREATE;
129                     i++;
130                     break;
131                 }
132             case 'R':
133                 {
134                     *pms += READ;
135                     i++;
136                     break;
137                 }
138             case 'U':
139                 {
140                     *pms += UPDATE;
141                     i++;
142                     break;
143                 }
144             case 'D':
145                 {
146                     *pms += DELETE;
147                     i++;
148                     break;
149                 }
150             case 'N':
151                 {
152                     *pms += NOTIFY;
153                     i++;
154                     break;
155                 }
156             case '_':
157                 {
158                     i++;
159                     break;
160                 }
161             default:
162                 {
163                     return -1;
164                 }
165         }
166     }
167     return 0;
168 }
169
170 /**
171  * Get the ACL property from user
172  *
173  * @param[in]    ACL Datastructure to save user inputs
174  * @return  0 on success otherwise -1.
175  */
176 static int InputACL(OicSecAcl_t *acl)
177 {
178     int ret;
179     char temp_id [UUID_LENGTH + 4] = {0,};
180     char temp_rsc[MAX_URI_LENGTH + 1] = {0,};
181     char temp_pms[MAX_PERMISSION_LENGTH + 1] = {0,};
182     printf("******************************************************************************\n");
183     printf("-Set ACL policy for target device\n");
184     printf("******************************************************************************\n");
185     //Set Subject.
186     printf("-URN identifying the subject\n");
187     printf("ex) doorDeviceUUID00 (16 Numbers except to '-')\n");
188     printf("Subject : ");
189     char *ptr = NULL;
190     ret = scanf("%19ms", &ptr);
191     if(1==ret)
192     {
193         OICStrcpy(temp_id, sizeof(temp_id), ptr);
194         OICFree(ptr);
195     }
196     else
197     {
198          printf("Error while input\n");
199          return -1;
200     }
201     int j = 0;
202     for (int i = 0; temp_id[i] != '\0'; i++)
203     {
204         if (DASH != temp_id[i])
205         {
206             if(j >= UUID_LENGTH)
207             {
208                 printf("Invalid input\n");
209                 return -1;
210             }
211             acl->subject.id[j++] = temp_id[i];
212         }
213     }
214
215     //Set Resource.
216     printf("Num. of Resource : \n");
217     ret = scanf("%zu", &acl->resourcesLen);
218     if(-1 == ret || MAX_ACL_LENGTH < acl->resourcesLen)
219     {
220         printf("Error while input\n");
221         return -1;
222     }
223     printf("-URI of resource\n");
224     printf("ex) /a/light (Max_URI_Length: 64 Byte )\n");
225     acl->resources = (char **)OICCalloc(acl->resourcesLen, sizeof(char *));
226     if (NULL == acl->resources)
227     {
228         OIC_LOG(ERROR, TAG, "Error while memory allocation");
229         return -1;
230     }
231     for (size_t i = 0; i < acl->resourcesLen; i++)
232     {
233         printf("[%zu]Resource : ", i + 1);
234         char *ptr_tempRsc = NULL;
235         ret = scanf("%64ms", &ptr_tempRsc);
236         if (1==ret)
237         {
238             OICStrcpy(temp_rsc, sizeof(temp_rsc), ptr_tempRsc);
239             OICFree(ptr_tempRsc);
240         }
241         else
242         {
243             printf("Error while input\n");
244             return -1;
245         }
246         acl->resources[i] = OICStrdup(temp_rsc);
247
248         if (NULL == acl->resources[i])
249         {
250             OIC_LOG(ERROR, TAG, "Error while memory allocation");
251             return -1;
252         }
253     }
254     // Set Permission
255     do
256     {
257         printf("-Set the permission(C,R,U,D,N)\n");
258         printf("ex) CRUDN, CRU_N,..(5 Charaters)\n");
259         printf("Permission : ");
260         char *ptr_temp_pms = NULL;
261         ret = scanf("%5ms", &ptr_temp_pms);
262         if(1 == ret)
263         {
264             OICStrcpy(temp_pms, sizeof(temp_pms), ptr_temp_pms);
265             OICFree(ptr_temp_pms);
266
267         }
268         else
269         {
270             printf("Error while input\n");
271             return -1;
272         }
273     }
274     while (0 != CalculateAclPermission(temp_pms, &(acl->permission)) );
275     // Set Rowner
276     printf("-URN identifying the rowner\n");
277     printf("ex) lightDeviceUUID0 (16 Numbers except to '-')\n");
278
279     printf("Rowner : ");
280     char *ptr_temp_id = NULL;
281     ret = scanf("%19ms", &ptr_temp_id);
282     if (1 == ret)
283     {
284         OICStrcpy(temp_id, sizeof(temp_id), ptr_temp_id);
285         OICFree(ptr_temp_id);
286     }
287     else
288     {
289         printf("Error while input\n");
290         return -1;
291     }
292     j = 0;
293     for (int k = 0; temp_id[k] != '\0'; k++)
294     {
295         if (DASH != temp_id[k])
296         {
297             acl->rownerID.id[j++] = temp_id[k];
298         }
299     }
300     return 0;
301 }
302
303
304 //FILE *client_fopen(const char *path, const char *mode)
305 FILE *client_fopen(const char* UNUSED_PARAM , const char *mode)
306 {
307     (void)UNUSED_PARAM;
308     return fopen(PROV_TOOL_DB_FILE, mode);
309 }
310
311 void PrintfResult(const char* procName, void* ctx, int nOfRes, OCProvisionResult_t *arr, bool hasError)
312 {
313     printf("-----------------------------------------------------------\n");
314     if(!hasError)
315     {
316         printf("%s was successfully done.\n", procName);
317     }
318     else
319     {
320         for(int i = 0; i < nOfRes; i++)
321         {
322             printf("UUID : ");
323             for(int j = 0; j < UUID_LENGTH; j++)
324             {
325                 printf("%c", arr[i].deviceId.id[j]);
326             }
327             printf("\t");
328             printf("Result=%d\n", arr[i].res);
329         }
330     }
331
332     if(ctx)
333     {
334         printf("Context is %s\n", (char*)ctx);
335     }
336     printf("-----------------------------------------------------------\n");
337 }
338
339 void ProvisionCertCB(void* ctx, int nOfRes, OCProvisionResult_t *arr, bool hasError)
340 {
341     if(!hasError)
342     {
343         gOwnershipState = 1;
344         PrintfResult("Provision Credential", ctx, nOfRes, arr, hasError);
345     }
346     else printf("Cert provisioning error\n-----------------------------------------");
347 }
348
349 void ProvisionAclCB(void* ctx, int nOfRes, OCProvisionResult_t *arr, bool hasError)
350 {
351     if(!hasError)
352     {
353         gOwnershipState = 1;
354         PrintfResult("Provision ACL", ctx, nOfRes, arr, hasError);
355     }
356 }
357
358 void ProvisionCrlCB(void* ctx, int nOfRes, OCProvisionResult_t *arr, bool hasError)
359 {
360     if(!hasError)
361     {
362         gOwnershipState = 1;
363         PrintfResult("Provision CRL", ctx, nOfRes, arr, hasError);
364     }
365 }
366
367
368
369 void OwnershipTransferCB(void* ctx, int nOfRes, OCProvisionResult_t *arr, bool hasError)
370 {
371     if(!hasError)
372     {
373         gOwnershipState = 1;
374         PrintfResult("Ownership transfer", ctx, nOfRes, arr, hasError);
375     }
376 }
377
378 static short IsCKMInfoFileExists()
379 {
380     FILE *ckmInf = fopen(CA_STORAGE_FILE, "r");
381     if (NULL != ckmInf)
382     {
383         fclose(ckmInf);
384         return 1;
385     }
386     return 0;
387 }
388
389 static PKIError InitCA()
390 {
391     FUNCTION_INIT();
392
393     if (IsCKMInfoFileExists())
394     {
395         CHECK_CALL(InitCKMInfo);
396     }
397     else
398     {
399         ByteArray rootName  = BYTE_ARRAY_INITIALIZER;
400         ByteArray CAPubKey  = BYTE_ARRAY_INITIALIZER;
401         ByteArray CAPrivKey = BYTE_ARRAY_INITIALIZER;
402         ByteArray rootCert  = BYTE_ARRAY_INITIALIZER;
403
404
405         uint8_t rootCertData[ISSUER_MAX_CERT_SIZE];
406         uint8_t CAPubKeyData[PUBLIC_KEY_SIZE];
407         uint8_t CAPrivKeyData[PRIVATE_KEY_SIZE];
408         const char rootNameStr[] = "Sample_Root";
409
410         CAPubKey.data  = CAPubKeyData;
411         CAPubKey.len   = PUBLIC_KEY_SIZE;
412         CAPrivKey.data = CAPrivKeyData;
413         CAPrivKey.len  = PRIVATE_KEY_SIZE;
414         rootCert.data  = rootCertData;
415         rootCert.len   = ISSUER_MAX_CERT_SIZE;
416         rootName.data  = (uint8_t *)rootNameStr;
417         rootName.len   = strlen(rootNameStr);
418
419         CHECK_CALL(SetRootName, rootName);
420         CHECK_CALL(GenerateCAKeyPair, &CAPrivKey, &CAPubKey);
421         CHECK_CALL(SetSerialNumber, 1);
422         CHECK_CALL(CKMIssueRootCertificate, NULL, NULL, &rootCert);
423         CHECK_CALL(SetCACertificate, &rootCert);
424     }
425
426     FUNCTION_CLEAR();
427 }
428
429 static int InputCRL(OicSecCrl_t *crlRes)
430 {
431     FUNCTION_INIT(
432             ByteArray crl = BYTE_ARRAY_INITIALIZER;
433             );
434
435     const int MAX_Revoked_NUMBER = 9;
436     uint8_t uint8ThisUpdateTime[DATE_LENGTH] = "130101000005Z";
437     uint32_t revokedNumbers[MAX_Revoked_NUMBER];
438     const uint8_t* revocationDates[MAX_Revoked_NUMBER];
439    // const uint8_t revocationDatesContent[MAX_Revoked_NUMBER][DATE_LENGTH];
440     uint32_t nuberOfRevoked = 0;
441     printf("Enter number of Revoked certificates(1..%d)\n", MAX_Revoked_NUMBER);
442     int ret = 0;
443     ret = scanf("%u", &nuberOfRevoked);
444     if(-1 == ret)
445     {
446         printf("Error while input\n");
447         return PKI_UNKNOWN_ERROR;
448     }
449
450     if((uint32_t)MAX_Revoked_NUMBER < nuberOfRevoked)
451     {
452         OIC_LOG(ERROR, TAG, "Wrong revoked certificate number");
453         return PKI_UNKNOWN_ERROR;
454     }
455
456     for (size_t i = 0; i < nuberOfRevoked; ++i)
457     {
458         printf("Revoked certificate %d:", i);
459         printf("Serial number (E. g.: 100):");
460         ret = scanf("%u", &revokedNumbers[i]);
461         if(-1 == ret)
462         {
463             printf("Error while input\n");
464             return PKI_UNKNOWN_ERROR;
465         }
466
467         revocationDates[i] = (const uint8_t*)"130101000005Z";
468     }
469
470     crl.len = CRL_MIN_SIZE + nuberOfRevoked * (sizeof(CertificateRevocationInfo_t) + 4)/* + 1000*/;
471     crl.data = (uint8_t *)OICCalloc(1, crl.len);
472
473     if (NULL == crl.data)
474     {
475         OIC_LOG(ERROR, TAG, "Error while memory allocation");
476         return PKI_MEMORY_ALLOC_FAILED;
477     }
478
479     CHECK_CALL(CKMIssueCRL, uint8ThisUpdateTime, nuberOfRevoked, revokedNumbers,
480             revocationDates, &crl);
481     PRINT_BYTE_ARRAY("CRL:\n",crl);
482     CHECK_CALL(SetCertificateRevocationList, &crl);
483     crlRes->CrlData = crl;
484     crlRes->ThisUpdate.data = uint8ThisUpdateTime;
485     crlRes->ThisUpdate.len = DATE_LENGTH;
486     crlRes->CrlId = 1;
487
488
489     FUNCTION_CLEAR(
490     //OICFree(crl.data);
491             );
492 }
493
494
495 /**
496  * Provisioning client sample using ProvisioningAPI
497  */
498 int main()
499 {
500     OCStackResult res = OC_STACK_OK;
501     OCProvisionDev_t* pDeviceList = NULL;
502     OCProvisionDev_t *pOwnedList = NULL;
503
504     // Initialize Persistent Storage for SVR database
505     OCPersistentStorage ps = { .open = client_fopen,
506                                .read = fread,
507                                .write = fwrite,
508                                .close = fclose,
509                                .unlink = unlink};
510
511     OCRegisterPersistentStorageHandler(&ps);
512
513     if (OC_STACK_OK != OCInit(NULL, 0, OC_CLIENT_SERVER))
514     {
515         OIC_LOG(ERROR, TAG, "OCStack init error");
516         goto error;
517     }
518     if(OC_STACK_OK != OCInitPM(PRVN_DB_FILE_NAME))
519     {
520         OIC_LOG(ERROR, TAG, "OC_PM init error");
521         goto error;
522     }
523
524     res = OCDiscoverUnownedDevices(PREDEFINED_TIMEOUT, &pDeviceList);
525     if(OC_STACK_OK != res)
526     {
527         OIC_LOG_V(ERROR, TAG, "Failed to PMDeviceDiscovery : %d", res);
528         goto error;
529     }
530
531     OCProvisionDev_t* pCurDev = pDeviceList;
532     int i;
533     while(pCurDev !=NULL)
534     {
535         for(i = 0; i < UUID_LENGTH; i++)
536         {
537             printf("%c", pCurDev->doxm->deviceID.id[i]);
538         }
539         printf("\n");
540         pCurDev = pCurDev->next;
541     }
542
543     //Register callback function to each OxM
544     OTMCallbackData_t justWorksCBData = {.loadSecretCB=NULL,
545                                          .createSecureSessionCB=NULL,
546                                          .createSelectOxmPayloadCB=NULL,
547                                          .createOwnerTransferPayloadCB=NULL};
548     justWorksCBData.loadSecretCB = LoadSecretJustWorksCallback;
549     justWorksCBData.createSecureSessionCB = CreateSecureSessionJustWorksCallback;
550     justWorksCBData.createSelectOxmPayloadCB = CreateJustWorksSelectOxmPayload;
551     justWorksCBData.createOwnerTransferPayloadCB = CreateJustWorksOwnerTransferPayload;
552     OTMSetOwnershipTransferCallbackData(OIC_JUST_WORKS, &justWorksCBData);
553
554     char* myContext = "OTM Context";
555     //Perform ownership transfer
556     res = OCDoOwnershipTransfer((void*)myContext, pDeviceList, OwnershipTransferCB);
557     if(OC_STACK_OK == res)
558     {
559         OIC_LOG(INFO, TAG, "Request for ownership transfer is sent successfully.");
560     }
561     else
562     {
563         OIC_LOG_V(ERROR, TAG, "Failed to OCDoOwnershipTransfer : %d", res);
564     }
565
566     gOwnershipState = 0;
567     while (gOwnershipState == 0)
568     {
569         if (OCProcess() != OC_STACK_OK)
570         {
571             OIC_LOG(ERROR, TAG, "OCStack process error");
572             goto error;
573         }
574         sleep(1);
575     }
576
577 // Credential & ACL provisioning between two devices.
578
579     OCProvisionDev_t *pOwnedDevices [MAX_OWNED_DEVICE] = {0,};
580     int nOwnedDevice = 0;
581
582     res = OCDiscoverOwnedDevices(PREDEFINED_TIMEOUT, &pOwnedList);
583     if (OC_STACK_OK == res)
584     {
585         printf("################## Owned Device List #######################\n");
586         while (pOwnedList != NULL)
587         {
588             nOwnedDevice ++;
589             printf(" %d : ", nOwnedDevice);
590             for (int i = 0; i < UUID_LENGTH; i++)
591             {
592                 printf("%c", pOwnedList->doxm->deviceID.id[i]);
593             }
594             printf("\n");
595             pOwnedDevices[nOwnedDevice] = pOwnedList;
596             pOwnedList = pOwnedList->next;
597         }
598     }
599     else
600     {
601         OIC_LOG(ERROR, TAG, "Error while Owned Device Discovery");
602     }
603
604     int Device1 = 0;
605     int Device2 = 0;
606     int ret = 0;
607
608     printf("Select 2 devices for Credential & ACL provisioning\n");
609     printf("Device 1: ");
610     ret = scanf("%d", &Device1);
611     if(-1 == ret)
612     {
613         printf("Error while input\n");
614         goto error;
615     }
616
617     printf("Device 2: ");
618     ret = scanf("%d", &Device2);
619     if(-1 == ret)
620     {
621         printf("Error while input\n");
622         goto error;
623     }
624
625     if( 0 > Device1 || 0 > Device2 || Device1 > nOwnedDevice || Device2 > nOwnedDevice)
626     {
627         OIC_LOG(ERROR, TAG, "Wrong devices number");
628         goto error;
629     }
630
631     gAcl = (OicSecAcl_t *)OICCalloc(1,sizeof(OicSecAcl_t));
632     if (NULL == gAcl)
633     {
634         OIC_LOG(ERROR, TAG, "Error while memory allocation");
635         goto error;
636     }
637
638     if (PKI_SUCCESS != InitCA())
639     {
640         OIC_LOG(ERROR, TAG, "CA init error");
641         goto error;
642     }
643
644
645     char *ctx = "DUMMY";
646
647     res = OCProvisionCredentials(ctx, SIGNED_ASYMMETRIC_KEY, 0, pOwnedDevices[Device1],
648                                                                 NULL, ProvisionCertCB);
649     if (OC_STACK_OK != res) OIC_LOG_V(ERROR, TAG, "Failed to provision Device 1 : %d", res);
650     gOwnershipState = 0;
651     while ( gOwnershipState == 0 )
652     {
653         if (OCProcess() != OC_STACK_OK)
654         {
655             OIC_LOG(ERROR, TAG, "OCStack process error");
656             goto error;
657         }
658         sleep(1);
659     }
660
661     res = OCProvisionCredentials(ctx, SIGNED_ASYMMETRIC_KEY, 0, pOwnedDevices[Device2],
662                                                                 NULL, ProvisionCertCB);
663     if (OC_STACK_OK != res)
664     {
665         OIC_LOG_V(ERROR, TAG, "Failed to provision Device 2 : %d", res);
666     }
667
668     gOwnershipState = 0;
669     while (gOwnershipState == 0)
670     {
671         if (OCProcess() != OC_STACK_OK)
672         {
673             OIC_LOG(ERROR, TAG, "OCStack process error");
674             goto error;
675         }
676         sleep(1);
677     }
678
679     printf("Input ACL for Device2\n");
680     if (0 == InputACL(gAcl))
681     {
682         printf("Success Input ACL\n");
683     }
684     else
685     {
686         OIC_LOG(ERROR, TAG, "InputACL error");
687         goto error;
688     }
689     res = OCProvisionACL(ctx, pOwnedDevices[Device2], gAcl, &ProvisionAclCB);
690     if (OC_STACK_OK != res)
691     {
692         OIC_LOG_V(ERROR, TAG, "Failed to ACL provision Device 2 : %d", res);
693     }
694
695     gOwnershipState = 0;
696     while (gOwnershipState == 0)
697     {
698         if (OCProcess() != OC_STACK_OK)
699         {
700             OIC_LOG(ERROR, TAG, "OCStack process error");
701             goto error;
702         }
703         sleep(1);
704     }
705     gCrl = (OicSecCrl_t *)OICMalloc(sizeof(OicSecCrl_t));
706
707     if (NULL == gCrl)
708     {
709         OIC_LOG(ERROR, TAG, "Error while memory allocation");
710         goto error;
711     }
712
713     if (PKI_SUCCESS != InputCRL(gCrl))
714     {
715         OIC_LOG(ERROR, TAG, "CA init error");
716         goto error;
717     }
718
719     PRINT_BYTE_ARRAY("gCrl = \n", gCrl->CrlData);
720
721     res = OCProvisionCRL(ctx, pOwnedDevices[Device2], gCrl, &ProvisionCrlCB);
722     if (OC_STACK_OK != res) OIC_LOG_V(ERROR, TAG, "Failed to CRL provision Device 2 : %d", res);
723
724     gOwnershipState = 0;
725     while (gOwnershipState == 0)
726     {
727         if (OCProcess() != OC_STACK_OK)
728         {
729             OIC_LOG(ERROR, TAG, "OCStack process error");
730             goto error;
731         }
732         sleep(1);
733     }
734
735     if (OCStop() != OC_STACK_OK)
736     {
737         OIC_LOG(ERROR, TAG, "OCStack process error");
738         goto error;
739     }
740
741 error:
742     deleteACL(gAcl);
743     OCDeleteDiscoveredDevices(pDeviceList);
744     OCDeleteDiscoveredDevices(pOwnedList);
745
746     return 0;
747 }