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 "idm_session.h"
17 
18 #include <inttypes.h>
19 #include "securec.h"
20 
21 #include "adaptor_algorithm.h"
22 #include "adaptor_log.h"
23 #include "adaptor_memory.h"
24 #include "adaptor_time.h"
25 #include "coauth.h"
26 #include "linked_list.h"
27 #include "idm_database.h"
28 
29 #define SESSION_VALIDITY_PERIOD (10 * 60 * 1000)
30 #define MAX_CHALLENGE_GENERATION_TIMES 5
31 
32 #ifdef IAM_TEST_ENABLE
33 #define IAM_STATIC
34 #else
35 #define IAM_STATIC static
36 #endif
37 
38 // User IDM session information.
39 struct SessionInfo {
40     int32_t userId;
41     uint32_t authType;
42     uint64_t time;
43     uint64_t validAuthTokenTime;
44     uint8_t challenge[CHALLENGE_LEN];
45     uint64_t scheduleId;
46     bool isUpdate;
47     bool isScheduleValid;
48 } *g_session;
49 
50 IAM_STATIC Buffer *g_cacheRootSecret = NULL;
51 
DestroyCacheRootSecret(void)52 IAM_STATIC void DestroyCacheRootSecret(void)
53 {
54     DestoryBuffer(g_cacheRootSecret);
55     g_cacheRootSecret = NULL;
56 }
57 
IsSessionExist(void)58 IAM_STATIC bool IsSessionExist(void)
59 {
60     if (g_session == NULL) {
61         LOG_INFO("the session does not exist");
62         return false;
63     }
64     return true;
65 }
66 
GenerateChallenge(uint8_t * challenge,uint32_t challengeLen)67 IAM_STATIC ResultCode GenerateChallenge(uint8_t *challenge, uint32_t challengeLen)
68 {
69     for (uint32_t i = 0; i < MAX_CHALLENGE_GENERATION_TIMES; ++i) {
70         if (SecureRandom(challenge, challengeLen) != RESULT_SUCCESS) {
71             LOG_ERROR("get challenge failed");
72             return RESULT_GENERAL_ERROR;
73         }
74         for (uint32_t j = 0; j < challengeLen; j++) {
75             if (challenge[j] != 0) {
76                 return RESULT_SUCCESS;
77             }
78         }
79         LOG_INFO("challenge is invalid, get again.");
80     }
81     LOG_ERROR("a rare failture");
82     return RESULT_GENERAL_ERROR;
83 }
84 
OpenEditSession(int32_t userId,uint8_t * challenge,uint32_t challengeLen)85 ResultCode OpenEditSession(int32_t userId, uint8_t *challenge, uint32_t challengeLen)
86 {
87     if (challenge == NULL || challengeLen != CHALLENGE_LEN) {
88         LOG_ERROR("challenge is null");
89         return RESULT_BAD_PARAM;
90     }
91     (void)memset_s(challenge, CHALLENGE_LEN, 0, CHALLENGE_LEN);
92     if (IsSessionExist()) {
93         (void)CloseEditSession();
94     }
95     g_session = Malloc(sizeof(struct SessionInfo));
96     if (g_session == NULL) {
97         LOG_ERROR("g_session malloc failed");
98         return RESULT_NO_MEMORY;
99     }
100     if (memset_s(g_session, sizeof(struct SessionInfo), 0, sizeof(struct SessionInfo)) != EOK) {
101         LOG_ERROR("g_session set failed");
102         Free(g_session);
103         g_session = NULL;
104         return RESULT_GENERAL_ERROR;
105     }
106     g_session->userId = userId;
107     ResultCode ret = GenerateChallenge(g_session->challenge, CHALLENGE_LEN);
108     if (ret != RESULT_SUCCESS) {
109         LOG_ERROR("failed to generate challenge");
110         Free(g_session);
111         g_session = NULL;
112         return ret;
113     }
114     g_session->time = GetSystemTime();
115     g_session->validAuthTokenTime = g_session->time;
116 
117     if (memcpy_s(challenge, CHALLENGE_LEN, g_session->challenge, CHALLENGE_LEN) != EOK) {
118         LOG_ERROR("failed to copy challenge");
119         Free(g_session);
120         g_session = NULL;
121         return RESULT_BAD_COPY;
122     }
123     g_session->isScheduleValid = false;
124     return RESULT_SUCCESS;
125 }
126 
RefreshValidTokenTime(void)127 void RefreshValidTokenTime(void)
128 {
129     if (!IsSessionExist()) {
130         LOG_ERROR("session is invalid");
131         return;
132     }
133     g_session->validAuthTokenTime = GetSystemTime();
134 }
135 
IsValidTokenTime(uint64_t tokenTime)136 bool IsValidTokenTime(uint64_t tokenTime)
137 {
138     if (!IsSessionExist()) {
139         LOG_ERROR("session is invalid");
140         return false;
141     }
142     return tokenTime >= g_session->validAuthTokenTime;
143 }
144 
CloseEditSession(void)145 ResultCode CloseEditSession(void)
146 {
147     if (!IsSessionExist()) {
148         return RESULT_GENERAL_ERROR;
149     }
150     DestroyCacheRootSecret();
151     ClearCachePin(g_session->userId);
152     Free(g_session);
153     g_session = NULL;
154     return RESULT_SUCCESS;
155 }
156 
GetUserId(int32_t * userId)157 ResultCode GetUserId(int32_t *userId)
158 {
159     if (userId == NULL || !IsSessionExist()) {
160         LOG_ERROR("param is invalid");
161         return RESULT_BAD_PARAM;
162     }
163     *userId = g_session->userId;
164     return RESULT_SUCCESS;
165 }
166 
CheckChallenge(uint8_t * challenge,uint32_t challengeLen)167 ResultCode CheckChallenge(uint8_t *challenge, uint32_t challengeLen)
168 {
169     if (challenge == NULL || challengeLen != CHALLENGE_LEN) {
170         LOG_ERROR("param is invalid");
171         return RESULT_BAD_PARAM;
172     }
173     if (!IsSessionExist()) {
174         LOG_ERROR("param is invalid");
175         return RESULT_NEED_INIT;
176     }
177     if (memcmp(challenge, g_session->challenge, CHALLENGE_LEN) != EOK) {
178         LOG_ERROR("failed to compare challenge");
179         return RESULT_BAD_MATCH;
180     }
181     return RESULT_SUCCESS;
182 }
183 
AssociateCoauthSchedule(uint64_t scheduleId,uint32_t authType,bool isUpdate)184 ResultCode AssociateCoauthSchedule(uint64_t scheduleId, uint32_t authType, bool isUpdate)
185 {
186     if (!IsSessionExist()) {
187         return RESULT_NEED_INIT;
188     }
189     g_session->scheduleId = scheduleId;
190     g_session->authType = authType;
191     g_session->isUpdate = isUpdate;
192     g_session->isScheduleValid = true;
193     return RESULT_SUCCESS;
194 }
195 
BreakOffCoauthSchedule(void)196 void BreakOffCoauthSchedule(void)
197 {
198     if (!IsSessionExist()) {
199         return;
200     }
201     if (g_session->isScheduleValid) {
202         RemoveCoAuthSchedule(g_session->scheduleId);
203     }
204     g_session->isScheduleValid = false;
205 }
206 
GetEnrollScheduleInfo(uint64_t * scheduleId,uint32_t * authType)207 ResultCode GetEnrollScheduleInfo(uint64_t *scheduleId, uint32_t *authType)
208 {
209     if (scheduleId == NULL || authType == NULL) {
210         LOG_ERROR("param is null");
211         return RESULT_BAD_PARAM;
212     }
213     if (!IsSessionExist() || g_session->isScheduleValid == false) {
214         return RESULT_NEED_INIT;
215     }
216     *scheduleId = g_session->scheduleId;
217     *authType = g_session->authType;
218     return RESULT_SUCCESS;
219 }
220 
CheckSessionTimeout(void)221 ResultCode CheckSessionTimeout(void)
222 {
223     if (!IsSessionExist()) {
224         return RESULT_NEED_INIT;
225     }
226     uint64_t currentTime = GetSystemTime();
227     if (currentTime < g_session->time) {
228         LOG_ERROR("bad time, currentTime: %{public}" PRIu64 ", sessionTime: %{public}" PRIu64,
229             currentTime, g_session->time);
230         return RESULT_GENERAL_ERROR;
231     }
232     if (currentTime - g_session->time > SESSION_VALIDITY_PERIOD) {
233         LOG_ERROR("timeout, currentTime: %{public}" PRIu64 ", sessionTime: %{public}" PRIu64,
234             currentTime, g_session->time);
235         DestroyCacheRootSecret();
236         ClearCachePin(g_session->userId);
237         return RESULT_TIMEOUT;
238     }
239     return RESULT_SUCCESS;
240 }
241 
GetIsUpdate(bool * isUpdate)242 ResultCode GetIsUpdate(bool *isUpdate)
243 {
244     if (isUpdate == NULL) {
245         LOG_ERROR("param is invalid");
246         return RESULT_BAD_PARAM;
247     }
248     if (!IsSessionExist() || g_session->isScheduleValid == false) {
249         LOG_ERROR("session need init");
250         return RESULT_NEED_INIT;
251     }
252     *isUpdate = g_session->isUpdate;
253     return RESULT_SUCCESS;
254 }
255 
CheckSessionValid(int32_t userId)256 ResultCode CheckSessionValid(int32_t userId)
257 {
258     ResultCode ret = CheckSessionTimeout();
259     if (ret != RESULT_SUCCESS) {
260         return ret;
261     }
262     if (g_session->userId != userId) {
263         return RESULT_GENERAL_ERROR;
264     }
265     return RESULT_SUCCESS;
266 }
267 
CacheRootSecret(int32_t userId,Buffer * rootSecret)268 void CacheRootSecret(int32_t userId, Buffer *rootSecret)
269 {
270     /* The presence of a session is the pin change phase */
271     if (CheckSessionTimeout() != RESULT_SUCCESS) {
272         return;
273     }
274     if (g_session->userId != userId) {
275         LOG_ERROR("CacheRootSecret check user id fail");
276         return;
277     }
278     if (!CheckBufferWithSize(rootSecret, ROOT_SECRET_LEN)) {
279         LOG_ERROR("check root secret fail");
280         return;
281     }
282     DestroyCacheRootSecret();
283     g_cacheRootSecret = CopyBuffer(rootSecret);
284     if (g_cacheRootSecret == NULL) {
285         LOG_ERROR("copy cache root secret fail");
286     }
287 }
288 
GetCacheRootSecret(int32_t userId)289 Buffer *GetCacheRootSecret(int32_t userId)
290 {
291     if (CheckSessionTimeout() != RESULT_SUCCESS) {
292         return NULL;
293     }
294     if (g_session->userId != userId) {
295         LOG_ERROR("GetCacheRootSecret check user id fail");
296         return NULL;
297     }
298     if (g_cacheRootSecret == NULL) {
299         LOG_ERROR("no cache root secret");
300         return NULL;
301     }
302     return CopyBuffer(g_cacheRootSecret);
303 }
304 
GetChallenge(uint8_t * challenge,uint32_t challengeLen)305 ResultCode GetChallenge(uint8_t *challenge, uint32_t challengeLen)
306 {
307     if ((challenge == NULL) || (challengeLen != CHALLENGE_LEN)) {
308         LOG_ERROR("challenge is invalid");
309         return RESULT_BAD_PARAM;
310     }
311 
312     ResultCode ret = CheckSessionTimeout();
313     if (ret != RESULT_SUCCESS) {
314         LOG_ERROR("session does not exist");
315         return ret;
316     }
317     if (memcpy_s(challenge, challengeLen, g_session->challenge, CHALLENGE_LEN) != EOK) {
318         LOG_ERROR("copy challenge failed");
319         return RESULT_GENERAL_ERROR;
320     }
321 
322     return RESULT_SUCCESS;
323 }
324 
IsValidUserType(int32_t userType)325 ResultCode IsValidUserType(int32_t userType)
326 {
327     if (userType != MAIN_USER && userType != SUB_USER && userType != PRIVATE_USER) {
328         LOG_ERROR("userType is invalid");
329         return RESULT_BAD_PARAM;
330     }
331     LOG_INFO("userType is valid");
332     return RESULT_SUCCESS;
333 }