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