1/*
2 * Copyright (c) 2021-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#include "appspawn_message.h"
16
17#include <errno.h>
18#include <limits.h>
19#include <stdlib.h>
20#include <string.h>
21
22#ifdef __LINUX__
23#include <linux/capability.h>
24#else
25#include <sys/capability.h>
26#endif
27
28#include "appspawn_server.h"
29#include "cJSON.h"
30#include "ohos_errno.h"
31#include "securec.h"
32
33static const size_t MAX_BUNDLE_NAME_LEN = 127;
34static const size_t MIN_BUNDLE_NAME_LEN = 7;
35static const size_t MAX_IDENTITY_ID_LEN = 24;
36static const size_t MIN_IDENTITY_ID_LEN = 1;
37static const size_t MAX_CAPABILITY_COUNT = 10;
38
39void FreeMessageSt(MessageSt *targetSt)
40{
41    if (targetSt != NULL) {
42        if (targetSt->bundleName != NULL) {
43            free(targetSt->bundleName);
44            targetSt->bundleName = NULL;
45        }
46
47        if (targetSt->identityID != NULL) {
48            free(targetSt->identityID);
49            targetSt->identityID = NULL;
50        }
51
52        if (targetSt->caps != NULL) {
53            free(targetSt->caps);
54            targetSt->caps = NULL;
55        }
56
57        targetSt->uID = -1;
58        targetSt->gID = -1;
59        targetSt->capsCnt = 0;
60    }
61}
62
63static enum OHOSLiteErrorCode ReadStringItem(cJSON *strItem, char **buf, size_t maxLen, size_t minLen)
64{
65    if (strItem == NULL || !cJSON_IsString(strItem)) {
66        return EC_INVALID;
67    }
68
69    char *strPtr = cJSON_GetStringValue(strItem);
70    if (strPtr == NULL) {
71        return EC_PROTOCOL;
72    }
73
74    size_t strLength = strlen(strPtr);
75    if (strLength > maxLen || strLength < minLen) {
76        return EC_PROTOCOL;
77    }
78
79    char *bufTmp = (char *)malloc(strLength + 1);
80    if (bufTmp == NULL) {
81        return EC_NOMEMORY;
82    }
83
84    if (strLength > 0 && memcpy_s(bufTmp, strLength, strPtr, strLength) != EOK) {
85        free(bufTmp);
86        bufTmp = NULL;
87        return EC_FAILURE;
88    }
89
90    bufTmp[strLength] = '\0';
91    *buf = bufTmp;
92    return EC_SUCCESS;
93}
94
95static double ReadNumberItem(cJSON *strItem)
96{
97    if (strItem == NULL || !cJSON_IsNumber(strItem)) {
98        return -1;
99    }
100
101    return cJSON_GetNumberValue(strItem);
102}
103
104static int GetCaps(const cJSON *curItem, MessageSt *msgSt)
105{
106    msgSt->capsCnt = 0;
107    msgSt->caps = NULL;
108    cJSON *capItem = cJSON_GetObjectItem(curItem, "capability");
109    if (!cJSON_IsArray(capItem)) {
110        APPSPAWN_LOGE("[appspawn] GetCaps failed, no caps array found.");
111        return EC_INVALID;
112    }
113
114    // caps array empty, means do not need any capability
115    int capsCnt = cJSON_GetArraySize(capItem);
116    if (capsCnt <= 0) {
117        return EC_SUCCESS;
118    }
119
120    if (capsCnt > MAX_CAPABILITY_COUNT) {
121        APPSPAWN_LOGE("[appspawn] GetCaps, too many caps[cnt %d], max %d",
122            capsCnt, MAX_CAPABILITY_COUNT);
123        return EC_INVALID;
124    }
125
126    msgSt->caps = (unsigned int *)malloc(sizeof(unsigned int) * capsCnt);
127    if (msgSt->caps == NULL) {
128        APPSPAWN_LOGE("[appspawn] GetCaps, malloc failed! capsCnt[cnt %d].", capsCnt);
129        return EC_NOMEMORY;
130    }
131
132    for (int i = 0; i < capsCnt; ++i) {
133        cJSON *capJ = cJSON_GetArrayItem(capItem, i);
134        if (!cJSON_IsNumber(capJ) || cJSON_GetNumberValue(capJ) < 0) {
135            APPSPAWN_LOGE("[appspawn] GetCaps, invalid cap value detected!");
136            free(msgSt->caps);
137            msgSt->caps = NULL;
138            return EC_INVALID;
139        }
140        msgSt->caps[i] = (unsigned int)cJSON_GetNumberValue(capJ);
141        if (msgSt->caps[i] > CAP_LAST_CAP) {
142            APPSPAWN_LOGE("[appspawn] GetCaps, invalid cap value %u detected!", \
143                msgSt->caps[i]);
144            free(msgSt->caps);
145            msgSt->caps = NULL;
146            return EC_INVALID;
147        }
148    }
149
150    msgSt->capsCnt = capsCnt;
151    return EC_SUCCESS;
152}
153
154int SplitMessage(const char *msg, unsigned int msgLen, MessageSt *msgSt)
155{
156    if (msgSt == NULL) {
157        return EC_INVALID;
158    }
159
160    if (msg == NULL || msgLen == 0) {
161        FreeMessageSt(msgSt);
162        return EC_INVALID;
163    }
164
165    cJSON *rootJ = cJSON_ParseWithLength(msg, msgLen);
166    if (rootJ == NULL) {
167        FreeMessageSt(msgSt);
168        return EC_PROTOCOL;
169    }
170
171    cJSON *bundleNameItem = cJSON_GetObjectItem(rootJ, "bundleName");
172    int ret = (int)ReadStringItem(bundleNameItem, &(msgSt->bundleName), MAX_BUNDLE_NAME_LEN, MIN_BUNDLE_NAME_LEN);
173    if (ret != EC_SUCCESS) {
174        FreeMessageSt(msgSt);
175        cJSON_Delete(rootJ);
176        return ret;
177    }
178
179    cJSON *identityIDItem = cJSON_GetObjectItem(rootJ, "identityID");
180    ret = (int)ReadStringItem(identityIDItem, &(msgSt->identityID), MAX_IDENTITY_ID_LEN, MIN_IDENTITY_ID_LEN);
181    if (ret != EC_SUCCESS) {
182        FreeMessageSt(msgSt);
183        cJSON_Delete(rootJ);
184        return ret;
185    }
186
187    cJSON *uIDItem = cJSON_GetObjectItem(rootJ, "uID");
188    cJSON *gIDItem = cJSON_GetObjectItem(rootJ, "gID");
189    msgSt->uID = (int)ReadNumberItem(uIDItem);
190    msgSt->gID = (int)ReadNumberItem(gIDItem);
191
192    ret = GetCaps(rootJ, msgSt);
193    if (ret != EC_SUCCESS) {
194        FreeMessageSt(msgSt);
195        cJSON_Delete(rootJ);
196        return ret;
197    }
198
199    cJSON_Delete(rootJ);
200
201    if (msgSt->uID <= 0 || msgSt->gID <= 0 || msgSt->uID == INT_MAX || msgSt->gID == INT_MAX) {
202        FreeMessageSt(msgSt);
203        return EC_PROTOCOL;
204    }
205    return EC_SUCCESS;
206}
207