• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *    http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "cert_manager_uri.h"
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include "securec.h"
23 
24 #include "cm_log.h"
25 #include "cm_util.h"
26 
27 #ifdef __cplusplus
28 extern "C" {
29 #endif
30 
31 #define IS_TYPE_VALID(t) ((t) <= CM_URI_TYPE_MAX)
32 
33 #define SCHEME "oh:"
34 #define P_OBJECT "o="
35 #define P_TYPE "t="
36 #define P_USER "u="
37 #define P_APP "a="
38 #define Q_MAC "m="
39 #define Q_CLIENT_USER "cu="
40 #define Q_CLIENT_APP "ca="
41 
42 // characters do not need to be encoded in path, other than digits and algabets
43 #define P_RES_AVAIL "-._~:[]@!$'()*+,=&"
44 // characters do not need to be encoded in query, other than digits and algabets
45 #define Q_RES_AVAIL "-._~:[]@!$'()*+,=/?|"
46 
CertManagerFreeUri(struct CMUri * uri)47 int32_t CertManagerFreeUri(struct CMUri *uri)
48 {
49     if (uri == NULL) {
50         return CMR_OK;
51     }
52     CM_FREE_PTR(uri->object);
53     CM_FREE_PTR(uri->user);
54     CM_FREE_PTR(uri->app);
55     CM_FREE_PTR(uri->mac);
56     CM_FREE_PTR(uri->clientUser);
57     CM_FREE_PTR(uri->clientApp);
58     return CMR_OK;
59 }
60 
CertManagerIsKeyObjectType(uint32_t type)61 inline bool CertManagerIsKeyObjectType(uint32_t type)
62 {
63     return (type == CM_URI_TYPE_APP_KEY || type == CM_URI_TYPE_WLAN_KEY);
64 }
65 
IsUnreserved(const char * resAvail,size_t resAvailLen,char c)66 static int IsUnreserved(const char *resAvail, size_t resAvailLen, char c)
67 {
68     if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
69         return 1;
70     }
71     if (resAvail != NULL) {
72         for (size_t i = 0; i < resAvailLen; i++) {
73             if (c == resAvail[i]) {
74                 return 1;
75             }
76         }
77     }
78     return 0;
79 }
80 
GetComponentEncodedLen(const char * key,const char * value,const char * resAvail,uint32_t * sep)81 static uint32_t GetComponentEncodedLen(const char *key, const char *value,
82     const char *resAvail, uint32_t *sep)
83 {
84     if (value == NULL) {
85         return 0;
86     }
87     size_t resAvailLen = strlen(resAvail);
88     size_t keyLen = strlen(key);
89     size_t valueLen = strlen(value);
90     size_t reserved = 0;
91     for (size_t i = 0; i < valueLen; i++) {
92         if (!IsUnreserved(resAvail, resAvailLen, value[i])) {
93             reserved++;
94         }
95     }
96     // each reserved character requires 2 extra bytes to percent-encode
97     uint32_t len = (uint32_t) (keyLen + valueLen + reserved * 2 + *sep);
98     *sep = 1;
99     return len;
100 }
101 
GetEncodedLen(const struct CMUri * uri)102 static uint32_t GetEncodedLen(const struct CMUri *uri)
103 {
104     if (uri == NULL) {
105         return 0;
106     }
107 
108     uint32_t sep = 0;
109     uint32_t len = strlen(SCHEME);
110 
111     len += GetComponentEncodedLen(P_TYPE, g_types[uri->type], P_RES_AVAIL, &sep);
112     len += GetComponentEncodedLen(P_OBJECT, uri->object, P_RES_AVAIL, &sep);
113     len += GetComponentEncodedLen(P_USER, uri->user, P_RES_AVAIL, &sep);
114     len += GetComponentEncodedLen(P_APP, uri->app, P_RES_AVAIL, &sep);
115 
116     uint32_t qlen = 0;
117     sep = 0;
118     qlen += GetComponentEncodedLen(Q_CLIENT_USER, uri->clientUser, Q_RES_AVAIL, &sep);
119     qlen += GetComponentEncodedLen(Q_CLIENT_APP, uri->clientApp, Q_RES_AVAIL, &sep);
120     qlen += GetComponentEncodedLen(Q_MAC, uri->mac, Q_RES_AVAIL, &sep);
121 
122     return len + sep + qlen;
123 }
124 
125 // encode the last 4 bits of an integer to a hex char
HexEncode(uint32_t v)126 static inline uint32_t HexEncode(uint32_t v)
127 {
128     v &= 0xf;
129     if (v < DEC_LEN) {
130         return ('0' + v);
131     } else {
132         return ('A' + v - DEC_LEN);
133     }
134 }
135 
EncodeComp(char * buf,uint32_t * offset,uint32_t * available,const char * key,const char * value,const char * resAvail,uint32_t * sep,char sepChar)136 static int32_t EncodeComp(
137     char *buf, uint32_t *offset, uint32_t *available,
138     const char *key, const char *value,
139     const char *resAvail,
140     uint32_t *sep, char sepChar)
141 {
142     if (value == NULL) {
143         return CMR_OK;
144     }
145 
146     size_t resAvailLen = strlen(resAvail);
147     size_t keyLen = strlen(key);
148     size_t valueLen = strlen(value);
149     uint32_t off = *offset;
150     uint32_t avail = *available;
151 
152     if (avail < *sep + keyLen + valueLen) {
153         return CMR_ERROR;
154     }
155 
156     if (*sep) {
157         buf[off] = sepChar;
158         off++;
159     }
160 
161     if (memcpy_s(buf + off, avail, key, keyLen) != EOK) {
162         return CMR_ERROR;
163     }
164     off += keyLen;
165     avail -= keyLen;
166 
167     for (size_t i = 0; i < valueLen; i++) {
168         if (IsUnreserved(resAvail, resAvailLen, value[i])) {
169             if (avail < 1) {
170                 return CMR_ERROR;
171             }
172             buf[off] = value[i];
173             off++;
174             avail--;
175         } else {
176             // each percent-encoded character requires 3 bytes
177             if (avail < 3) {
178                 return CMR_ERROR;
179             }
180             buf[off] = '%';
181             off++;
182             buf[off] = (char) HexEncode(value[i] >> 4); // higher 4 bits of the char byte
183             off++;
184             buf[off] = (char) HexEncode(value[i]); // lower 4 bits of the char byte
185             off++;
186             // each percent-encoded character requires 3 bytes
187             avail -= 3;
188         }
189     }
190 
191     *sep = 1;
192     *offset = off;
193     *available = avail;
194     return CMR_OK;
195 }
196 
EncodePathComp(char * encoded,uint32_t * offset,uint32_t * availLen,const struct CMUri * uri)197 static int32_t EncodePathComp(char *encoded, uint32_t *offset, uint32_t *availLen,
198     const struct CMUri *uri)
199 {
200     int32_t ret = CM_FAILURE;
201     uint32_t sep = 0;
202     uint32_t off = *offset;
203     uint32_t avail = *availLen;
204 
205     do {
206         ret = EncodeComp(encoded, &off, &avail, P_TYPE, g_types[uri->type], P_RES_AVAIL, &sep, ';');
207         if (ret != CM_SUCCESS) {
208             CM_LOG_E("encode <t=> failed");
209             break;
210         }
211 
212         ret = EncodeComp(encoded, &off, &avail, P_OBJECT, uri->object, P_RES_AVAIL, &sep, ';');
213         if (ret != CM_SUCCESS) {
214             CM_LOG_E("encode <o=> failed");
215             break;
216         }
217 
218         ret = EncodeComp(encoded, &off, &avail, P_USER, uri->user, P_RES_AVAIL, &sep, ';');
219         if (ret != CM_SUCCESS) {
220             CM_LOG_E("encode <u=> failed");
221             break;
222         }
223 
224         ret = EncodeComp(encoded, &off, &avail, P_APP, uri->app, P_RES_AVAIL, &sep, ';');
225         if (ret != CM_SUCCESS) {
226             CM_LOG_E("encode <a=> failed");
227             break;
228         }
229     } while (0);
230 
231     *offset = off;
232     *availLen = avail;
233     return ret;
234 }
235 
EncodeQueryComp(char * encoded,uint32_t * offset,uint32_t * availLen,const struct CMUri * uri)236 static int32_t EncodeQueryComp(char *encoded, uint32_t *offset, uint32_t *availLen,
237     const struct CMUri *uri)
238 {
239     if (uri->clientUser == NULL && uri->clientApp == NULL && uri->mac == NULL) {
240         // no query. we are done.
241         return CM_SUCCESS;
242     }
243 
244     int32_t ret = CM_FAILURE;
245     uint32_t sep = 0;
246     uint32_t off = *offset;
247     uint32_t avail = *availLen;
248     encoded[off] = '?';
249     off++;
250     avail--;
251 
252     do {
253         ret = EncodeComp(encoded, &off, &avail, Q_CLIENT_USER, uri->clientUser, Q_RES_AVAIL, &sep, '&');
254         if (ret != CM_SUCCESS) {
255             CM_LOG_E("encode <cu=> failed");
256             break;
257         }
258 
259         ret = EncodeComp(encoded, &off, &avail, Q_CLIENT_APP, uri->clientApp, Q_RES_AVAIL, &sep, '&');
260         if (ret != CM_SUCCESS) {
261             CM_LOG_E("encode <ca=> failed");
262             break;
263         }
264 
265         ret = EncodeComp(encoded, &off, &avail, Q_MAC, uri->mac, Q_RES_AVAIL, &sep, '&');
266         if (ret != CM_SUCCESS) {
267             CM_LOG_E("encode <m=> failed");
268             break;
269         }
270     } while (0);
271 
272     *offset = off;
273     *availLen = avail;
274     return ret;
275 }
276 
CertManagerUriEncode(char * encoded,uint32_t * encodedLen,const struct CMUri * uri)277 int32_t CertManagerUriEncode(char *encoded, uint32_t *encodedLen, const struct CMUri *uri)
278 {
279     if (encodedLen == NULL || uri == NULL || !IS_TYPE_VALID(uri->type)) {
280         CM_LOG_E("input params is invaild");
281         return CMR_ERROR_INVALID_ARGUMENT;
282     }
283 
284     uint32_t encLen = GetEncodedLen(uri) + 1;
285     if (encoded == NULL) {
286         *encodedLen = encLen;
287         return CM_SUCCESS;
288     }
289 
290     if (*encodedLen < encLen) {
291         CM_LOG_W("Buffer to small for encoded URI (%u < %u).\n", *encodedLen, encLen);
292         return CMR_ERROR_BUFFER_TOO_SMALL;
293     }
294 
295     uint32_t off = 0;
296     uint32_t avail = *encodedLen;
297 
298     if (memcpy_s(encoded, avail, SCHEME, strlen(SCHEME)) != EOK) {
299         return CM_FAILURE;
300     }
301     off += strlen(SCHEME);
302     avail -= strlen(SCHEME);
303 
304     int32_t ret = EncodePathComp(encoded, &off, &avail, uri);
305     if (ret != CM_SUCCESS) {
306         return ret;
307     }
308 
309     ret = EncodeQueryComp(encoded, &off, &avail, uri);
310     if (ret != CM_SUCCESS) {
311         return ret;
312     }
313 
314     *encodedLen = off;
315     return CM_SUCCESS;
316 }
317 
HexDecode(uint32_t h)318 static uint32_t HexDecode(uint32_t h)
319 {
320     h &= 0xff;
321     if (h >= '0' && h <= '9') {
322         return h - '0';
323     }
324     if (h >= 'a' && h <= 'f') {
325         return h - 'a' + DEC_LEN;
326     }
327     if (h >= 'A' && h <= 'F') {
328         return h - 'A' + DEC_LEN;
329     }
330     return 0;
331 }
332 
HexDecode2(uint32_t h1,uint32_t h2)333 static inline uint32_t HexDecode2(uint32_t h1, uint32_t h2)
334 {
335     return ((HexDecode(h1) << 4) | HexDecode(h2)) & 0xff; /* 4 is number of shifts */
336 }
337 
IndexOf(char sep,const char * data,uint32_t start,uint32_t end)338 static inline uint32_t IndexOf(char sep, const char *data, uint32_t start, uint32_t end)
339 {
340     for (uint32_t i = start; i < end; i++) {
341         if (data[i] == sep) {
342             return i;
343         }
344     }
345     return end;
346 }
347 
DecodeValue(const char * s,uint32_t off,uint32_t len)348 static char *DecodeValue(const char *s, uint32_t off, uint32_t len)
349 {
350     if (s == NULL || len == 0 || len > MAX_AUTH_LEN_URI) {
351         CM_LOG_E("input value failed");
352         return NULL;
353     }
354     char *buf = MALLOC(len + 1);
355     if (buf == NULL) {
356         CM_LOG_E("malloc buf failed");
357         return NULL;
358     }
359     (void)memset_s(buf, len + 1, 0, len + 1);
360 
361     uint32_t bufOff = 0;
362     for (uint32_t i = off; i < off + len; i++, bufOff++) {
363         if (s[i] != '%') {
364             buf[bufOff] = s[i];
365         } else if ((i + 2) < (off + len)) { /* 2 is to be accessed byte count */
366             buf[bufOff] = HexDecode2(s[i + 1], s[i + 2]); /* 2 is array index */
367             i += 2; /* 2 is array index */
368         } else {
369             CM_LOG_E("path has special character, but len is invalid");
370             free(buf);
371             return NULL;
372         }
373     }
374     char *ret = strndup(buf, bufOff);
375     free(buf);
376     return ret;
377 }
378 
DecodeEnum(const char * s,uint32_t off,uint32_t len,const char * values[],uint32_t valueCount)379 static uint32_t DecodeEnum(const char *s, uint32_t off, uint32_t len, const char *values[], uint32_t valueCount)
380 {
381     for (uint32_t i = 0; i < valueCount; i++) {
382         size_t valLen = strlen(values[i]);
383         if (valLen == len && memcmp(s + off, values[i], len) == 0) {
384             return i;
385         }
386     }
387     // no match found, default value is an invalid enum value
388     return valueCount + 1;
389 }
390 
DecodePath(struct CMUri * uri,const char * path,uint32_t start,uint32_t end)391 static int32_t DecodePath(struct CMUri *uri, const char *path, uint32_t start, uint32_t end)
392 {
393     while (start < end) {
394         uint32_t i = IndexOf(';', path, start, end);
395         if (i <= start) {
396             // something is wrong
397             CM_LOG_W("Invalid uri path\n");
398             return CMR_ERROR_INVALID_ARGUMENT;
399         }
400 
401         uint32_t valueOff = 0;
402         uint32_t valueLen = 0;
403 
404         // for string field
405         char **field = NULL;
406 
407         // for enum field
408         uint32_t *e = NULL;
409         const char **values = NULL;
410         uint32_t valueCount = 0;
411 
412         if (!strncmp(P_OBJECT, path + start, strlen(P_OBJECT))) {
413             valueOff = start + strlen(P_OBJECT);
414             valueLen = i - start - strlen(P_OBJECT);
415             field = &uri->object;
416         } else if (!strncmp(P_TYPE, path + start, strlen(P_TYPE))) {
417             valueOff = start + strlen(P_TYPE);
418             valueLen = i - start - strlen(P_TYPE);
419             e = &uri->type;
420             values = g_types;
421             valueCount = TYPE_COUNT;
422         } else if (!strncmp(P_USER, path + start, strlen(P_USER))) {
423             valueOff = start + strlen(P_USER);
424             valueLen = i - start - strlen(P_USER);
425             field = &uri->user;
426         } else if (!strncmp(P_APP, path + start, strlen(P_APP))) {
427             valueOff = start + strlen(P_APP);
428             valueLen = i - start - strlen(P_APP);
429             field = &uri->app;
430         }
431 
432         if (field != NULL) {
433             if (valueLen == 0) {
434                 *field = NULL;
435             } else {
436                 *field = DecodeValue(path, valueOff, valueLen);
437             }
438         } else if (e != NULL) {
439             *e = DecodeEnum(path, valueOff, valueLen, values, valueCount);
440         } else {
441             CM_LOG_W("Invalid field in path\n");
442             return CMR_ERROR_INVALID_ARGUMENT;
443         }
444 
445         start = i + 1;
446     }
447 
448     return CMR_OK;
449 }
450 
DecodeQuery(struct CMUri * uri,const char * query,uint32_t start,uint32_t end)451 static int32_t DecodeQuery(struct CMUri *uri, const char *query, uint32_t start, uint32_t end)
452 {
453     while (start < end) {
454         uint32_t i = IndexOf('&', query, start, end);
455         if (i <= start) {
456             // something is wrong
457             CM_LOG_W("Invalid uri query\n");
458             return CMR_ERROR_INVALID_ARGUMENT;
459         }
460 
461         uint32_t valueOff = 0;
462         uint32_t valueLen = 0;
463         char **field = NULL;
464         if (!strncmp(Q_CLIENT_USER, query + start, strlen(Q_CLIENT_USER))) {
465             valueOff = start + strlen(Q_CLIENT_USER);
466             valueLen = i - start - strlen(Q_CLIENT_USER);
467             field = &uri->clientUser;
468         } else if (!strncmp(Q_CLIENT_APP, query + start, strlen(Q_CLIENT_APP))) {
469             valueOff = start + strlen(Q_CLIENT_APP);
470             valueLen = i - start - strlen(Q_CLIENT_APP);
471             field = &uri->clientApp;
472         } else if (!strncmp(Q_MAC, query + start, strlen(Q_MAC))) {
473             valueOff = start + strlen(Q_MAC);
474             valueLen = i - start - strlen(Q_MAC);
475             field = &uri->mac;
476         }
477 
478         if (field != NULL) {
479             if (valueLen == 0) {
480                 *field = NULL;
481             } else {
482                 *field = DecodeValue(query, valueOff, valueLen);
483             }
484         } else {
485             CM_LOG_W("Invalid field in query\n");
486             return CMR_ERROR_INVALID_ARGUMENT;
487         }
488 
489         start = i + 1;
490     }
491     return CMR_OK;
492 }
493 
CertManagerUriDecode(struct CMUri * uri,const char * encoded)494 int32_t CertManagerUriDecode(struct CMUri *uri, const char *encoded)
495 {
496     if (uri == NULL || encoded == NULL) {
497         CM_LOG_E("input params is invaild");
498         return CMR_ERROR_INVALID_ARGUMENT;
499     }
500 
501     (void)memset_s(uri, sizeof(*uri), 0, sizeof(*uri));
502     uri->type = CM_URI_TYPE_INVALID;
503 
504     uint32_t len = strlen(encoded);
505     if (len > MAX_AUTH_LEN_URI) {
506         CM_LOG_E("invalid uri len[%u]", len);
507         return CMR_ERROR_INVALID_ARGUMENT;
508     }
509 
510     uint32_t off = 0;
511     if (len < strlen(SCHEME) || memcmp(encoded, SCHEME, strlen(SCHEME))) {
512         CM_LOG_E("Scheme mismatch. Not a cert manager URI");
513         return CMR_ERROR_INVALID_ARGUMENT;
514     }
515     off += strlen(SCHEME);
516 
517     uint32_t pathStart = off;
518     uint32_t pathEnd = IndexOf('?', encoded, off, len);
519     uint32_t queryStart = (pathEnd == len) ? len : pathEnd + 1;
520     uint32_t queryEnd = len;
521 
522     int32_t ret = DecodePath(uri, encoded, pathStart, pathEnd);
523     if (ret != CM_SUCCESS) {
524         CertManagerFreeUri(uri);
525         return ret;
526     }
527 
528     ret = DecodeQuery(uri, encoded, queryStart, queryEnd);
529     if (ret != CM_SUCCESS) {
530         CertManagerFreeUri(uri);
531         return ret;
532     }
533 
534     return CM_SUCCESS;
535 }
536 
CertManagerGetUidFromUri(const struct CmBlob * uri,uint32_t * uid)537 int32_t CertManagerGetUidFromUri(const struct CmBlob *uri, uint32_t *uid)
538 {
539     struct CMUri uriObj;
540     (void)memset_s(&uriObj, sizeof(uriObj), 0, sizeof(uriObj));
541     int32_t ret = CertManagerUriDecode(&uriObj, (char *)uri->data);
542     if (ret != CM_SUCCESS) {
543         CM_LOG_E("uri decode failed, ret = %d", ret);
544         return ret;
545     }
546 
547     if (uriObj.app == NULL) {
548         CM_LOG_E("uri app invalid");
549         (void)CertManagerFreeUri(&uriObj);
550         return CMR_ERROR_INVALID_ARGUMENT;
551     }
552 
553     if (CmIsNumeric(uriObj.app, strlen(uriObj.app) + 1, uid) != CM_SUCCESS) {
554         CM_LOG_E("parse string to uint32 failed.");
555         (void)CertManagerFreeUri(&uriObj);
556         return CMR_ERROR_INVALID_ARGUMENT;
557     }
558 
559     (void)CertManagerFreeUri(&uriObj);
560     return CM_SUCCESS;
561 }
562 
CmConstructUri(const struct CMUri * uriObj,struct CmBlob * outUri)563 int32_t CmConstructUri(const struct CMUri *uriObj, struct CmBlob *outUri)
564 {
565     uint32_t outLen = 0;
566     int32_t ret = CertManagerUriEncode(NULL, &outLen, uriObj);
567     if (ret != CM_SUCCESS) {
568         CM_LOG_E("get uriObj len failed, ret = %d", ret);
569         return ret;
570     }
571 
572     if ((outLen == 0) || (outLen > MAX_OUT_BLOB_SIZE)) {
573         CM_LOG_E("invalid outLen[%u]", outLen);
574         return CMR_ERROR_INVALID_OPERATION;
575     }
576 
577     char *data = (char *)CMMalloc(outLen);
578     if (data == NULL) {
579         CM_LOG_E("malloc uri buf failed");
580         return CMR_ERROR_INVALID_OPERATION;
581     }
582     (void)memset_s(data, outLen, 0, outLen);
583     outUri->size = outLen; /* include 1 byte: the terminator('\0')  */
584 
585     ret = CertManagerUriEncode(data, &outLen, uriObj); /* outLen not include '\0' */
586     if (ret != CM_SUCCESS) {
587         CM_LOG_E("encord uri failed");
588         outUri->size = 0;
589         CMFree(data);
590         return ret;
591     }
592 
593     outUri->data = (uint8_t *)data;
594     return CM_SUCCESS;
595 }
596 
UintToStr(uint32_t input,char * out,uint32_t outLen)597 static int32_t UintToStr(uint32_t input, char *out, uint32_t outLen)
598 {
599     if (snprintf_s(out, outLen, outLen - 1, "%u", input) < 0) {
600         return CMR_ERROR_INVALID_OPERATION;
601     }
602     return CM_SUCCESS;
603 }
604 
CmConstructCommonUri(const struct CmContext * context,const uint32_t type,const struct CmBlob * certAlias,struct CmBlob * outUri)605 int32_t CmConstructCommonUri(const struct CmContext *context, const uint32_t type,
606     const struct CmBlob *certAlias, struct CmBlob *outUri)
607 {
608     struct CMUri uriObj;
609     (void)memset_s(&uriObj, sizeof(struct CMUri), 0, sizeof(struct CMUri));
610 
611     char userIdStr[MAX_UINT32_LEN] = { 0 };
612     int32_t ret = UintToStr(context->userId, userIdStr, MAX_UINT32_LEN);
613     if (ret != CM_SUCCESS) {
614         CM_LOG_E("construct userId to str failed");
615         return ret;
616     }
617 
618     char uidStr[MAX_UINT32_LEN] = { 0 };
619     ret = UintToStr(context->uid, uidStr, MAX_UINT32_LEN);
620     if (ret != CM_SUCCESS) {
621         CM_LOG_E("construct uid to str failed");
622         return ret;
623     }
624 
625     uriObj.object = (char *)certAlias->data;
626     uriObj.type = type;
627     uriObj.user = userIdStr;
628     uriObj.app = uidStr;
629 
630     ret = CmConstructUri(&uriObj, outUri);
631     if (ret != CM_SUCCESS) {
632         CM_LOG_E("construct uri failed, ret = %d", ret);
633     }
634     return ret;
635 }
636 
637 #ifdef __cplusplus
638 }
639 #endif
640 
641