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
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 if ((i + 2) < (off + len)) { /* 2 is to be accessed byte count */
365 buf[bufOff] = HexDecode2(s[i + 1], s[i + 2]); /* 2 is array index */
366 i += 2; /* 2 is array index */
367 } else {
368 CM_LOG_E("path has special character, but len is invalid");
369 free(buf);
370 return NULL;
371 }
372 }
373 char *ret = strndup(buf, bufOff);
374 free(buf);
375 return ret;
376 }
377
DecodeEnum(const char * s,uint32_t off,uint32_t len,const char * values[],uint32_t valueCount)378 static uint32_t DecodeEnum(const char *s, uint32_t off, uint32_t len, const char *values[], uint32_t valueCount)
379 {
380 for (uint32_t i = 0; i < valueCount; i++) {
381 size_t valLen = strlen(values[i]);
382 if (valLen == len && memcmp(s + off, values[i], len) == 0) {
383 return i;
384 }
385 }
386 // no match found, default value is an invalid enum value
387 return valueCount + 1;
388 }
389
DecodePath(struct CMUri * uri,const char * path,uint32_t start,uint32_t end)390 static int32_t DecodePath(struct CMUri *uri, const char *path, uint32_t start, uint32_t end)
391 {
392 while (start < end) {
393 uint32_t i = IndexOf(';', path, start, end);
394 if (i <= start) {
395 // something is wrong
396 CM_LOG_W("Invalid uri path\n");
397 return CMR_ERROR_INVALID_ARGUMENT;
398 }
399
400 uint32_t valueOff = 0;
401 uint32_t valueLen = 0;
402
403 // for string field
404 char **field = NULL;
405
406 // for enum field
407 uint32_t *e = NULL;
408 const char **values = NULL;
409 uint32_t valueCount = 0;
410
411 if (!strncmp(P_OBJECT, path + start, strlen(P_OBJECT))) {
412 valueOff = start + strlen(P_OBJECT);
413 valueLen = i - start - strlen(P_OBJECT);
414 field = &uri->object;
415 } else if (!strncmp(P_TYPE, path + start, strlen(P_TYPE))) {
416 valueOff = start + strlen(P_TYPE);
417 valueLen = i - start - strlen(P_TYPE);
418 e = &uri->type;
419 values = g_types;
420 valueCount = TYPE_COUNT;
421 } else if (!strncmp(P_USER, path + start, strlen(P_USER))) {
422 valueOff = start + strlen(P_USER);
423 valueLen = i - start - strlen(P_USER);
424 field = &uri->user;
425 } else if (!strncmp(P_APP, path + start, strlen(P_APP))) {
426 valueOff = start + strlen(P_APP);
427 valueLen = i - start - strlen(P_APP);
428 field = &uri->app;
429 }
430
431 if (field != NULL) {
432 if (valueLen == 0) {
433 *field = NULL;
434 } else {
435 *field = DecodeValue(path, valueOff, valueLen);
436 }
437 } else if (e != NULL) {
438 *e = DecodeEnum(path, valueOff, valueLen, values, valueCount);
439 } else {
440 CM_LOG_W("Invalid field in path\n");
441 return CMR_ERROR_INVALID_ARGUMENT;
442 }
443
444 start = i + 1;
445 }
446
447 return CMR_OK;
448 }
449
DecodeQuery(struct CMUri * uri,const char * query,uint32_t start,uint32_t end)450 static int32_t DecodeQuery(struct CMUri *uri, const char *query, uint32_t start, uint32_t end)
451 {
452 while (start < end) {
453 uint32_t i = IndexOf('&', query, start, end);
454 if (i <= start) {
455 // something is wrong
456 CM_LOG_W("Invalid uri query\n");
457 return CMR_ERROR_INVALID_ARGUMENT;
458 }
459
460 uint32_t valueOff = 0;
461 uint32_t valueLen = 0;
462 char **field = NULL;
463 if (!strncmp(Q_CLIENT_USER, query + start, strlen(Q_CLIENT_USER))) {
464 valueOff = start + strlen(Q_CLIENT_USER);
465 valueLen = i - start - strlen(Q_CLIENT_USER);
466 field = &uri->clientUser;
467 } else if (!strncmp(Q_CLIENT_APP, query + start, strlen(Q_CLIENT_APP))) {
468 valueOff = start + strlen(Q_CLIENT_APP);
469 valueLen = i - start - strlen(Q_CLIENT_APP);
470 field = &uri->clientApp;
471 } else if (!strncmp(Q_MAC, query + start, strlen(Q_MAC))) {
472 valueOff = start + strlen(Q_MAC);
473 valueLen = i - start - strlen(Q_MAC);
474 field = &uri->mac;
475 }
476
477 if (field != NULL) {
478 if (valueLen == 0) {
479 *field = NULL;
480 } else {
481 *field = DecodeValue(query, valueOff, valueLen);
482 }
483 } else {
484 CM_LOG_W("Invalid field in query\n");
485 return CMR_ERROR_INVALID_ARGUMENT;
486 }
487
488 start = i + 1;
489 }
490 return CMR_OK;
491 }
492
CertManagerUriDecode(struct CMUri * uri,const char * encoded)493 int32_t CertManagerUriDecode(struct CMUri *uri, const char *encoded)
494 {
495 if (uri == NULL || encoded == NULL) {
496 CM_LOG_E("input params is invaild");
497 return CMR_ERROR_INVALID_ARGUMENT;
498 }
499
500 (void)memset_s(uri, sizeof(*uri), 0, sizeof(*uri));
501 uri->type = CM_URI_TYPE_INVALID;
502
503 uint32_t len = strlen(encoded);
504 if (len > MAX_AUTH_LEN_URI) {
505 CM_LOG_E("invalid uri len[%u]", len);
506 return CMR_ERROR_INVALID_ARGUMENT;
507 }
508
509 uint32_t off = 0;
510 if (len < strlen(SCHEME) || memcmp(encoded, SCHEME, strlen(SCHEME))) {
511 CM_LOG_E("Scheme mismatch. Not a cert manager URI");
512 return CMR_ERROR_INVALID_ARGUMENT;
513 }
514 off += strlen(SCHEME);
515
516 uint32_t pathStart = off;
517 uint32_t pathEnd = IndexOf('?', encoded, off, len);
518 uint32_t queryStart = (pathEnd == len) ? len : pathEnd + 1;
519 uint32_t queryEnd = len;
520
521 int32_t ret = DecodePath(uri, encoded, pathStart, pathEnd);
522 if (ret != CM_SUCCESS) {
523 CertManagerFreeUri(uri);
524 return ret;
525 }
526
527 ret = DecodeQuery(uri, encoded, queryStart, queryEnd);
528 if (ret != CM_SUCCESS) {
529 CertManagerFreeUri(uri);
530 return ret;
531 }
532
533 return CM_SUCCESS;
534 }
535
CertManagerGetUidFromUri(const struct CmBlob * uri,uint32_t * uid)536 int32_t CertManagerGetUidFromUri(const struct CmBlob *uri, uint32_t *uid)
537 {
538 struct CMUri uriObj;
539 (void)memset_s(&uriObj, sizeof(uriObj), 0, sizeof(uriObj));
540 int32_t ret = CertManagerUriDecode(&uriObj, (char *)uri->data);
541 if (ret != CM_SUCCESS) {
542 CM_LOG_E("uri decode failed, ret = %d", ret);
543 return ret;
544 }
545
546 if (uriObj.app == NULL) {
547 CM_LOG_E("uri app invalid");
548 (void)CertManagerFreeUri(&uriObj);
549 return CMR_ERROR_INVALID_ARGUMENT;
550 }
551
552 *uid = atoi(uriObj.app);
553 (void)CertManagerFreeUri(&uriObj);
554 return CM_SUCCESS;
555 }
556
CmConstructUri(const struct CMUri * uriObj,struct CmBlob * outUri)557 int32_t CmConstructUri(const struct CMUri *uriObj, struct CmBlob *outUri)
558 {
559 uint32_t outLen = 0;
560 int32_t ret = CertManagerUriEncode(NULL, &outLen, uriObj);
561 if (ret != CM_SUCCESS) {
562 CM_LOG_E("get uriObj len failed, ret = %d", ret);
563 return ret;
564 }
565
566 if ((outLen == 0) || (outLen > MAX_OUT_BLOB_SIZE)) {
567 CM_LOG_E("invalid outLen[%u]", outLen);
568 return CMR_ERROR_INVALID_OPERATION;
569 }
570
571 char *data = (char *)CMMalloc(outLen);
572 if (data == NULL) {
573 CM_LOG_E("malloc uri buf failed");
574 return CMR_ERROR_INVALID_OPERATION;
575 }
576 (void)memset_s(data, outLen, 0, outLen);
577 outUri->size = outLen; /* include 1 byte: the terminator('\0') */
578
579 ret = CertManagerUriEncode(data, &outLen, uriObj); /* outLen not include '\0' */
580 if (ret != CM_SUCCESS) {
581 CM_LOG_E("encord uri failed");
582 outUri->size = 0;
583 CMFree(data);
584 return ret;
585 }
586
587 outUri->data = (uint8_t *)data;
588 return CM_SUCCESS;
589 }
590
UintToStr(uint32_t input,char * out,uint32_t outLen)591 static int32_t UintToStr(uint32_t input, char *out, uint32_t outLen)
592 {
593 if (snprintf_s(out, outLen, outLen - 1, "%u", input) < 0) {
594 return CMR_ERROR_INVALID_OPERATION;
595 }
596 return CM_SUCCESS;
597 }
598
CmConstructCommonUri(const struct CmContext * context,const uint32_t type,const struct CmBlob * certAlias,struct CmBlob * outUri)599 int32_t CmConstructCommonUri(const struct CmContext *context, const uint32_t type,
600 const struct CmBlob *certAlias, struct CmBlob *outUri)
601 {
602 struct CMUri uriObj;
603 (void)memset_s(&uriObj, sizeof(struct CMUri), 0, sizeof(struct CMUri));
604
605 char userIdStr[MAX_UINT32_LEN] = { 0 };
606 int32_t ret = UintToStr(context->userId, userIdStr, MAX_UINT32_LEN);
607 if (ret != CM_SUCCESS) {
608 CM_LOG_E("construct userId to str failed");
609 return ret;
610 }
611
612 char uidStr[MAX_UINT32_LEN] = { 0 };
613 ret = UintToStr(context->uid, uidStr, MAX_UINT32_LEN);
614 if (ret != CM_SUCCESS) {
615 CM_LOG_E("construct uid to str failed");
616 return ret;
617 }
618
619 uriObj.object = (char *)certAlias->data;
620 uriObj.type = type;
621 uriObj.user = userIdStr;
622 uriObj.app = uidStr;
623
624 ret = CmConstructUri(&uriObj, outUri);
625 if (ret != CM_SUCCESS) {
626 CM_LOG_E("construct uri failed, ret = %d", ret);
627 }
628 return ret;
629 }
630
631 #ifdef __cplusplus
632 }
633 #endif
634
635