/* * Copyright (C) 2021-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ipc_dev_auth_stub.h" #include "common_defs.h" #include "hc_log.h" #include "ipc_adapt.h" #include "ipc_callback_stub.h" #include "ipc_sdk.h" #include "permission_adapter.h" #include "securec.h" #include "system_ability_definition.h" #include "hc_string_vector.h" #include "hidump_adapter.h" #include "string_ex.h" #ifdef DEV_AUTH_SERVICE_BUILD #include "account_auth_plugin_proxy.h" #include "data_manager.h" #include "hisysevent_adapter.h" #endif using namespace std; namespace OHOS { static std::mutex g_cBMutex; struct CbStubInfo { sptr cbStub; bool inUse; }; static struct CbStubInfo g_cbStub[MAX_CBSTUB_SIZE]; static bool g_cbStubInited = false; static const uint32_t RESTORE_CODE = 14701; #ifdef DEV_AUTH_SERVICE_BUILD static const uint32_t DEFAULT_UPGRADE_OS_ACCOUNT_ID = 100; #endif #define MAX_DATA_LEN 102400 ServiceDevAuth::ServiceDevAuth(bool serialInvokeFlag) : IRemoteStub(serialInvokeFlag) {} ServiceDevAuth::~ServiceDevAuth() { maxCallMapSz = MAX_CALLMAP_SIZE; if (callMapTable != nullptr) { delete[] callMapTable; callMapTable = nullptr; } callMapElemNum = 0; } int32_t ServiceDevAuth::Dump(int32_t fd, const std::vector& args) { std::vector strArgs; for (auto arg : args) { strArgs.emplace_back(Str16ToStr8(arg)); } uint32_t argc = strArgs.size(); StringVector strArgVec = CreateStrVector(); for (uint32_t i = 0; i < argc; i++) { HcString strArg = CreateString(); if (!StringSetPointer(&strArg, strArgs[i].c_str())) { LOGE("Failed to set strArg!"); DeleteString(&strArg); continue; } if (strArgVec.pushBackT(&strArgVec, strArg) == NULL) { LOGE("Failed to push strArg to strArgVec!"); DeleteString(&strArg); } } DEV_AUTH_DUMP(fd, &strArgVec); DestroyStrVector(&strArgVec); return 0; } IpcServiceCall ServiceDevAuth::GetCallMethodByMethodId(int32_t methodId) { int32_t i; if (callMapTable == nullptr) { return nullptr; } for (i = 0; i < maxCallMapSz; i++) { if ((callMapTable[i].methodId == methodId) && (callMapTable[i].method != nullptr)) { return callMapTable[i].method; } } return nullptr; } static int32_t DecodeCallRequest(MessageParcel &data, IpcDataInfo *paramsCache, int32_t cacheNum, int32_t &inParamNum) { int32_t dataLen = 0; int32_t i; int32_t ret; if (data.GetReadableBytes() == 0) { return HC_SUCCESS; } if (data.GetReadableBytes() > MAX_DATA_LEN) { LOGE("Data len over MAX_DATA_LEN"); return HC_ERR_IPC_BAD_MESSAGE_LENGTH; } if (data.GetReadableBytes() < sizeof(int32_t)) { LOGE("Insufficient data available in IPC container. [Data]: dataLen"); return HC_ERR_IPC_BAD_MESSAGE_LENGTH; } data.ReadInt32(dataLen); if (dataLen > static_cast(data.GetReadableBytes())) { LOGE("Insufficient data available in IPC container. [Data]: data"); return HC_ERR_IPC_BAD_MESSAGE_LENGTH; } if (data.GetReadableBytes() < sizeof(int32_t)) { LOGE("Insufficient data available in IPC container. [Data]: inParamNum"); return HC_ERR_IPC_BAD_MESSAGE_LENGTH; } data.ReadInt32(inParamNum); if ((inParamNum < 0) || (inParamNum > cacheNum)) { LOGE("param number invalid, inParamNum - %d", inParamNum); return HC_ERR_IPC_BAD_PARAM_NUM; } for (i = 0; i < inParamNum; i++) { ret = DecodeIpcData(reinterpret_cast(&data), &(paramsCache[i].type), &(paramsCache[i].val), &(paramsCache[i].valSz)); if (ret != HC_SUCCESS) { LOGE("decode failed, ret %d", ret); return ret; } } return HC_SUCCESS; } static int32_t GetMethodId(MessageParcel &data, int32_t &methodId) { if (data.GetDataSize() < sizeof(int32_t)) { LOGE("Insufficient data available in IPC container. [Data]: methodId"); return HC_ERR_IPC_CALL_DATA_LENGTH; } methodId = data.ReadInt32(); return HC_SUCCESS; } static void WithObject(int32_t methodId, MessageParcel &data, IpcDataInfo &ipcData, int32_t &cnt) { if (!IsCallbackMethod(methodId)) { return; } if (data.GetReadableBytes() < sizeof(int32_t)) { LOGE("Insufficient data available in IPC container. [Data]: type"); return; } ipcData.type = data.ReadInt32(); ipcData.valSz = sizeof(StubDevAuthCb); sptr tmp = data.ReadRemoteObject(); if (!tmp) { LOGE("should with remote object, but read failed"); return; } ipcData.idx = ServiceDevAuth::SetRemoteObject(tmp); if (ipcData.idx >= 0) { ipcData.val = reinterpret_cast(&(ipcData.idx)); LOGI("object trans success, set id %d", ipcData.idx); cnt++; } } static void InitCbStubTable() { int32_t i; if (g_cbStubInited) { return; } std::lock_guard autoLock(g_cBMutex); if (g_cbStubInited) { /* for first init at the same time */ return; } for (i = 0; i < MAX_CBSTUB_SIZE; i++) { g_cbStub[i].inUse = false; } g_cbStubInited = true; return; } static int32_t HandleRestoreCall(MessageParcel &data, MessageParcel &reply) { #ifdef DEV_AUTH_SERVICE_BUILD int32_t osAccountId = DEFAULT_UPGRADE_OS_ACCOUNT_ID; data.ReadInt32(osAccountId); LOGI("Begin to upgrade data for osAccountId: %d.", osAccountId); int32_t res = ExcuteCredMgrCmd(osAccountId, UPGRADE_DATA, nullptr, nullptr); ReloadOsAccountDb(osAccountId); if (res != HC_SUCCESS) { LOGE("Failed to upgrade data!"); DEV_AUTH_REPORT_FAULT_EVENT_WITH_ERR_CODE(UPGRADE_DATA_EVENT, PROCESS_UPDATE, res); } reply.WriteInt32(res); #else (void)data; (void)reply; #endif return 0; } int32_t ServiceDevAuth::HandleDeviceAuthCall(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) { SET_LOG_MODE(NORMAL_MODE); int32_t ret = HC_ERR_IPC_UNKNOW_OPCODE; uint32_t dataLen; int32_t methodId = 0; int32_t reqParamNum = 0; MessageParcel replyCache; IpcDataInfo reqParams[MAX_REQUEST_PARAMS_NUM] = { { 0 } }; IpcServiceCall serviceCall = nullptr; switch (code) { case static_cast(DevAuthInterfaceCode::DEV_AUTH_CALL_REQUEST): ret = GetMethodId(data, methodId); if (ret != HC_SUCCESS) { break; } if (CheckPermission(methodId) != HC_SUCCESS) { return -1; } serviceCall = GetCallMethodByMethodId(methodId); if (serviceCall == nullptr) { ret = HC_ERR_IPC_METHOD_ID_INVALID; break; } ret = DecodeCallRequest(data, reqParams, MAX_REQUEST_PARAMS_NUM, reqParamNum); if (ret != HC_SUCCESS) { break; } if (reqParamNum < (MAX_REQUEST_PARAMS_NUM - 1)) { InitCbStubTable(); WithObject(methodId, data, reqParams[reqParamNum], reqParamNum); } ret = serviceCall(reqParams, reqParamNum, reinterpret_cast(&replyCache)); break; default: return IPCObjectStub::OnRemoteRequest(code, data, reply, option); } reply.WriteInt32(ret); dataLen = replyCache.GetDataSize(); if (dataLen > 0) { reply.WriteInt32(dataLen); reply.WriteBuffer(reinterpret_cast(replyCache.GetData()), dataLen); } return 0; } int32_t ServiceDevAuth::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) { std::u16string readToken = data.ReadInterfaceToken(); bool isRestoreCall = ((code == RESTORE_CODE) && (readToken == std::u16string(u"OHOS.Updater.RestoreData"))); if (readToken != GetDescriptor() && !isRestoreCall) { LOGE("[IPC][C->S]: The proxy interface token is invalid!"); return -1; } if (isRestoreCall) { return HandleRestoreCall(data, reply); } else { return HandleDeviceAuthCall(code, data, reply, option); } } int32_t ServiceDevAuth::SetCallMap(IpcServiceCall method, int32_t methodId) { int32_t len; errno_t eno; IpcServiceCallMap *callMapTmp = nullptr; if ((1 + callMapElemNum) > maxCallMapSz) { maxCallMapSz += MAX_CALLMAP_SIZE; if (callMapTable != nullptr) { callMapTmp = callMapTable; callMapTable = nullptr; } } if (callMapTable == nullptr) { callMapTable = new(std::nothrow) IpcServiceCallMap[maxCallMapSz]; if (callMapTable == nullptr) { return HC_ERR_ALLOC_MEMORY; } len = sizeof(IpcServiceCallMap) * maxCallMapSz; (void)memset_s(callMapTable, len, 0, len); if (callMapTmp != nullptr) { eno = memcpy_s(callMapTable, len, callMapTmp, (sizeof(IpcServiceCallMap) * callMapElemNum)); if (eno != EOK) { delete[] callMapTable; callMapTable = callMapTmp; maxCallMapSz -= MAX_CALLMAP_SIZE; return HC_ERR_MEMORY_COPY; } delete[] callMapTmp; callMapTmp = nullptr; } } callMapTable[callMapElemNum].method = method; callMapTable[callMapElemNum].methodId = methodId; callMapElemNum++; return HC_SUCCESS; } int32_t ServiceDevAuth::SetRemoteObject(sptr &object) { int32_t idx = -1; int32_t i; std::lock_guard autoLock(g_cBMutex); for (i = 0; i < MAX_CBSTUB_SIZE; i++) { if (!g_cbStub[i].inUse) { idx = i; break; } } LOGI("remote object cache index %d", idx); if (idx == -1) { return -1; } g_cbStub[idx].cbStub = object; g_cbStub[idx].inUse = true; return idx; } void ServiceDevAuth::AddCbDeathRecipient(int32_t cbStubIdx, int32_t cbDataIdx) { bool bRet = false; if ((cbStubIdx < 0) || (cbStubIdx >= MAX_CBSTUB_SIZE) || (!g_cbStub[cbStubIdx].inUse)) { return; } std::lock_guard autoLock(g_cBMutex); bRet = g_cbStub[cbStubIdx].cbStub->AddDeathRecipient(new(std::nothrow) DevAuthDeathRecipient(cbDataIdx)); LOGI("AddDeathRecipient %s, callback stub idx %d", bRet ? "success" : "failed", cbStubIdx); return; } void ServiceDevAuth::ResetRemoteObject(int32_t idx) { if ((idx >= 0) && (idx < MAX_CBSTUB_SIZE)) { LOGI("remote object used done, idx %d", idx); std::lock_guard autoLock(g_cBMutex); g_cbStub[idx].inUse = false; } return; } void ServiceDevAuth::ActCallback(int32_t objIdx, int32_t callbackId, bool sync, uintptr_t cbHook, MessageParcel &dataParcel, MessageParcel &reply) { if ((objIdx < 0) || (objIdx >= MAX_CBSTUB_SIZE) || (!g_cbStub[objIdx].inUse)) { LOGW("nothing to do, callback id %d, remote object id %d", callbackId, objIdx); return; } MessageOption option(MessageOption::TF_SYNC); if (!sync) { option.SetFlags(MessageOption::TF_ASYNC); } std::lock_guard autoLock(g_cBMutex); sptr proxy = iface_cast(g_cbStub[objIdx].cbStub); proxy->DoCallBack(callbackId, cbHook, dataParcel, reply, option); return; } DevAuthDeathRecipient::DevAuthDeathRecipient(int32_t cbIdx) { callbackIdx = cbIdx; } void DevAuthDeathRecipient::OnRemoteDied(const wptr &remoteObject) { LOGI("remote is not actively, to reset local resource"); ResetIpcCallBackNodeByNodeId(callbackIdx); } }