• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #5035

24 Apr 2026 11:25AM UTC coverage: 73.06% (+0.002%) from 73.058%
#5035

push

travis-ci

web-flow
merge: from main to 3.0 branch #35224

merge: from main to 3.0 branch[manual-only]

1344 of 1975 new or added lines in 48 files covered. (68.05%)

14127 existing lines in 142 files now uncovered.

275902 of 377640 relevant lines covered (73.06%)

132208813.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.33
/source/dnode/mnode/impl/src/mndSecurityPolicy.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15

16
#define _DEFAULT_SOURCE
17
#include "mndSecurityPolicy.h"
18
#include "mndCluster.h"
19
#include "mndMnode.h"
20
#include "mndPrivilege.h"
21
#include "mndRole.h"
22
#include "mndShow.h"
23
#include "mndTrans.h"
24
#include "mndUser.h"
25

26
// SDB raw format version.  Bump when adding new persistent fields.
27
#define SEC_POLICY_VER_NUMBE 1
28

29
static SSdbRaw *mndSecPolicyActionEncode(SSecurityPolicyObj *pObj);
30
static SSdbRow *mndSecPolicyActionDecode(SSdbRaw *pRaw);
31
static int32_t  mndSecPolicyActionInsert(SSdb *pSdb, SSecurityPolicyObj *pObj);
32
static int32_t  mndSecPolicyActionDelete(SSdb *pSdb, SSecurityPolicyObj *pObj);
33
static int32_t  mndSecPolicyActionUpdate(SSdb *pSdb, SSecurityPolicyObj *pOld, SSecurityPolicyObj *pNew);
34
static int32_t  mndCreateDefaultSecurityPolicy(SMnode *pMnode);
35
static int32_t  mndRetrieveSecurityPolicies(SRpcMsg *pMsg, SShowObj *pShow, SSDataBlock *pBlock, int32_t rows);
36
static void     mndCancelGetNextSecurityPolicy(SMnode *pMnode, void *pIter);
37
static int32_t  mndProcessEnforceSodImpl(SMnode *pMnode);
38

39
int32_t mndInitSecurityPolicy(SMnode *pMnode) {
497,488✔
40
  SSdbTable table = {
497,488✔
41
      .sdbType = SDB_SECURITY_POLICY,
42
      .keyType = SDB_KEY_INT32,
43
      .deployFp = (SdbDeployFp)mndCreateDefaultSecurityPolicy,
44
      .encodeFp = (SdbEncodeFp)mndSecPolicyActionEncode,
45
      .decodeFp = (SdbDecodeFp)mndSecPolicyActionDecode,
46
      .insertFp = (SdbInsertFp)mndSecPolicyActionInsert,
47
      .updateFp = (SdbUpdateFp)mndSecPolicyActionUpdate,
48
      .deleteFp = (SdbDeleteFp)mndSecPolicyActionDelete,
49
  };
50

51
  mndAddShowRetrieveHandle(pMnode, TSDB_MGMT_TABLE_SECURITY_POLICIES, mndRetrieveSecurityPolicies);
497,488✔
52
  mndAddShowFreeIterHandle(pMnode, TSDB_MGMT_TABLE_SECURITY_POLICIES, mndCancelGetNextSecurityPolicy);
497,488✔
53

54
  // Initialize MAC state cache — starts disabled, updated on successful activation
55
  pMnode->macActive = MAC_MODE_DISABLED;
497,488✔
56

57
  return sdbSetTable(pMnode->pSdb, table);
497,488✔
58
}
59

60
void mndCleanupSecurityPolicy(SMnode *pMnode) {}
497,419✔
61

62
// ---- SDB encode/decode ----
63

64
static SSdbRaw *mndSecPolicyActionEncode(SSecurityPolicyObj *pObj) {
2,626,018✔
65
  int32_t code = 0;
2,626,018✔
66
  int32_t lino = 0;
2,626,018✔
67
  terrno = TSDB_CODE_OUT_OF_MEMORY;
2,626,018✔
68

69
  SSdbRaw *pRaw = sdbAllocRaw(SDB_SECURITY_POLICY, SEC_POLICY_VER_NUMBE, sizeof(SSecurityPolicyObj));
2,626,018✔
70
  if (pRaw == NULL) goto _OVER;
2,626,018✔
71

72
  int32_t dataPos = 0;
2,626,018✔
73
  SDB_SET_INT32(pRaw, dataPos, pObj->type, _OVER)
2,626,018✔
74
  SDB_SET_INT64(pRaw, dataPos, pObj->createdTime, _OVER)
2,626,018✔
75
  SDB_SET_INT64(pRaw, dataPos, pObj->updateTime, _OVER)
2,626,018✔
76
  SDB_SET_INT64(pRaw, dataPos, pObj->activateTime, _OVER)
2,626,018✔
77
  SDB_SET_UINT8(pRaw, dataPos, pObj->status, _OVER)
2,626,018✔
78
  SDB_SET_BINARY(pRaw, dataPos, pObj->activator, TSDB_USER_LEN, _OVER)
2,626,018✔
79
  SDB_SET_BINARY(pRaw, dataPos, pObj->reserve, sizeof(pObj->reserve), _OVER)
2,626,018✔
80
  SDB_SET_DATALEN(pRaw, dataPos, _OVER);
2,626,018✔
81

82
  terrno = 0;
2,626,018✔
83

84
_OVER:
2,626,018✔
85
  if (terrno != 0) {
2,626,018✔
NEW
86
    mError("secpolicy:%d, failed to encode to raw:%p since %s", pObj->type, pRaw, terrstr());
×
NEW
87
    sdbFreeRaw(pRaw);
×
NEW
88
    return NULL;
×
89
  }
90

91
  mTrace("secpolicy:%d, encode to raw:%p, row:%p", pObj->type, pRaw, pObj);
2,626,018✔
92
  return pRaw;
2,626,018✔
93
}
94

95
static SSdbRow *mndSecPolicyActionDecode(SSdbRaw *pRaw) {
995,304✔
96
  int32_t code = 0;
995,304✔
97
  int32_t lino = 0;
995,304✔
98
  terrno = TSDB_CODE_OUT_OF_MEMORY;
995,304✔
99
  SSecurityPolicyObj *pObj = NULL;
995,304✔
100
  SSdbRow            *pRow = NULL;
995,304✔
101

102
  int8_t sver = 0;
995,304✔
103
  if (sdbGetRawSoftVer(pRaw, &sver) != 0) goto _OVER;
995,304✔
104

105
  if (sver != SEC_POLICY_VER_NUMBE) {
995,304✔
NEW
106
    terrno = TSDB_CODE_SDB_INVALID_DATA_VER;
×
NEW
107
    goto _OVER;
×
108
  }
109

110
  pRow = sdbAllocRow(sizeof(SSecurityPolicyObj));
995,304✔
111
  if (pRow == NULL) goto _OVER;
995,304✔
112

113
  pObj = sdbGetRowObj(pRow);
995,304✔
114
  if (pObj == NULL) goto _OVER;
995,304✔
115

116
  int32_t dataPos = 0;
995,304✔
117
  SDB_GET_INT32(pRaw, dataPos, &pObj->type, _OVER)
995,304✔
118
  SDB_GET_INT64(pRaw, dataPos, &pObj->createdTime, _OVER)
995,304✔
119
  SDB_GET_INT64(pRaw, dataPos, &pObj->updateTime, _OVER)
995,304✔
120
  SDB_GET_INT64(pRaw, dataPos, &pObj->activateTime, _OVER)
995,304✔
121
  SDB_GET_UINT8(pRaw, dataPos, &pObj->status, _OVER)
995,304✔
122
  SDB_GET_BINARY(pRaw, dataPos, pObj->activator, TSDB_USER_LEN, _OVER)
995,304✔
123
  SDB_GET_BINARY(pRaw, dataPos, pObj->reserve, sizeof(pObj->reserve), _OVER)
995,304✔
124

125
  terrno = 0;
995,304✔
126

127
_OVER:
995,304✔
128
  if (terrno != 0) {
995,304✔
NEW
129
    mError("secpolicy:%d, failed to decode from raw:%p since %s", pObj == NULL ? 0 : pObj->type, pRaw, terrstr());
×
NEW
130
    taosMemoryFreeClear(pRow);
×
NEW
131
    return NULL;
×
132
  }
133

134
  mTrace("secpolicy:%d, decode from raw:%p, row:%p", pObj->type, pRaw, pObj);
995,304✔
135
  return pRow;
995,304✔
136
}
137

138
static int32_t mndSecPolicyActionInsert(SSdb *pSdb, SSecurityPolicyObj *pObj) {
994,944✔
139
  mTrace("secpolicy:%d, perform insert action, row:%p", pObj->type, pObj);
994,944✔
140
  // Sync MAC state cache: fires on startup SDB replay, restoring persisted state
141
  if (pObj->type == TSDB_SECURITY_POLICY_MAC) {
994,944✔
142
    pSdb->pMnode->macActive = pObj->status;
497,472✔
143
  }
144
  return 0;
994,944✔
145
}
146

147
static int32_t mndSecPolicyActionDelete(SSdb *pSdb, SSecurityPolicyObj *pObj) {
995,198✔
148
  mTrace("secpolicy:%d, perform delete action, row:%p", pObj->type, pObj);
995,198✔
149
  return 0;
995,198✔
150
}
151

152
static int32_t mndSecPolicyActionUpdate(SSdb *pSdb, SSecurityPolicyObj *pOld, SSecurityPolicyObj *pNew) {
360✔
153
  mTrace("secpolicy:%d, perform update action, old row:%p new row:%p", pOld->type, pOld, pNew);
360✔
154
  pOld->updateTime = pNew->updateTime;
360✔
155
  pOld->activateTime = pNew->activateTime;
360✔
156
  pOld->status = pNew->status;
360✔
157
  tstrncpy(pOld->activator, pNew->activator, sizeof(pOld->activator));
360✔
158
  (void)memcpy(pOld->reserve, pNew->reserve, sizeof(pOld->reserve));
360✔
159
  // Sync MAC state cache: fires when Raft commit-log is applied — the true success point
160
  if (pOld->type == TSDB_SECURITY_POLICY_MAC) {
360✔
161
    pSdb->pMnode->macActive = pOld->status;
180✔
162
  }
163
  return 0;
360✔
164
}
165

166
// ---- Deploy helpers ----
167

168
// Append a single policy row as a commit-log entry in pTrans.
169
static int32_t mndAppendPolicyToTrans(STrans *pTrans, int32_t policyType) {
714,860✔
170
  SSecurityPolicyObj obj = {0};
714,860✔
171
  obj.type = policyType;
714,860✔
172
  obj.createdTime = taosGetTimestampMs();
714,860✔
173
  obj.updateTime = obj.createdTime;
714,860✔
174
  obj.activateTime = obj.createdTime;
714,860✔
175
  // status defaults to 0 (SEC_POLICY_STATUS_DEFAULT) for both SOD and MAC
176

177
  SSdbRaw *pRaw = mndSecPolicyActionEncode(&obj);
714,860✔
178
  if (pRaw == NULL) return terrno;
714,860✔
179

180
  int32_t code = sdbSetRawStatus(pRaw, SDB_STATUS_READY);
714,860✔
181
  if (code != 0) {
714,860✔
NEW
182
    sdbFreeRaw(pRaw);
×
NEW
183
    return code;
×
184
  }
185

186
  code = mndTransAppendCommitlog(pTrans, pRaw);
714,860✔
187
  if (code != 0) {
714,860✔
188
    // pRaw ownership transferred on success; free only on failure
NEW
189
    sdbFreeRaw(pRaw);
×
NEW
190
    return code;
×
191
  }
192

193
  // Second sdbSetRawStatus marks it ready-for-commit (matches cluster deploy pattern)
194
  return sdbSetRawStatus(pRaw, SDB_STATUS_READY);
714,860✔
195
}
196

197
static int32_t mndCreateDefaultSecurityPolicy(SMnode *pMnode) {
357,430✔
198
  // One transaction creates both SOD and MAC rows.
199
  // key = int32 policyType, no dependency on clusterId → deploy order safe.
200
  STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_RETRY, TRN_CONFLICT_NOTHING, NULL, "create-security-policy");
357,430✔
201
  if (pTrans == NULL) return terrno;
357,430✔
202

203
  int32_t code = 0;
357,430✔
204
  if ((code = mndAppendPolicyToTrans(pTrans, TSDB_SECURITY_POLICY_SOD)) != 0) goto _OVER;
357,430✔
205
  if ((code = mndAppendPolicyToTrans(pTrans, TSDB_SECURITY_POLICY_MAC)) != 0) goto _OVER;
357,430✔
206

207
  mInfo("trans:%d, used to create default security policies (SOD + MAC)", pTrans->id);
357,430✔
208

209
  if ((code = mndTransPrepare(pMnode, pTrans)) != 0) {
357,430✔
NEW
210
    mError("trans:%d, failed to prepare since %s", pTrans->id, tstrerror(code));
×
211
  }
212

213
_OVER:
357,430✔
214
  mndTransDrop(pTrans);
357,430✔
215
  return code;
357,430✔
216
}
217

218
// ---- SDB acquire/release helpers ----
219

220
static SSecurityPolicyObj *mndAcquireSecPolicy(SMnode *pMnode, int32_t policyType) {
66,792✔
221
  return (SSecurityPolicyObj *)sdbAcquire(pMnode->pSdb, SDB_SECURITY_POLICY, &policyType);
66,792✔
222
}
223

224
static void mndReleaseSecPolicy(SMnode *pMnode, SSecurityPolicyObj *pObj) { sdbRelease(pMnode->pSdb, pObj); }
66,792✔
225

226
// ---- Accessor functions ----
227

228
int32_t mndGetClusterSoDMode(SMnode *pMnode) {
64,452✔
229
  int32_t             sodMode = SOD_MODE_ENABLED;
64,452✔
230
  SSecurityPolicyObj *pObj = mndAcquireSecPolicy(pMnode, TSDB_SECURITY_POLICY_SOD);
64,452✔
231
  if (pObj != NULL) {
64,452✔
232
    sodMode = pObj->status;
64,452✔
233
    mndReleaseSecPolicy(pMnode, pObj);
64,452✔
234
  }
235
  return sodMode;
64,452✔
236
}
237

238
// ---- show security_policies ----
239

240
static const char *_SoDMandatoryInfo[3][2] = {
241
    {"mandatory", "system is operational, root disabled permanently"},
242
    {"mandatory(initial)", "Initial phase: mandatory roles missing, only account setup operations are allowed"},
243
    {"mandatory(enforcing)", "Enforce phase: transitioning mode, account destructive operations are blocked"},
244
};
245

246
static int32_t mndRetrieveSecurityPolicies(SRpcMsg *pMsg, SShowObj *pShow, SSDataBlock *pBlock, int32_t rows) {
1,075✔
247
  SMnode             *pMnode = pMsg->info.node;
1,075✔
248
  SSdb               *pSdb = pMnode->pSdb;
1,075✔
249
  int32_t             code = 0, lino = 0;
1,075✔
250
  int32_t             numOfRows = 0;
1,075✔
251
  SSecurityPolicyObj *pObj = NULL;
1,075✔
252
  char                buf[128 + VARSTR_HEADER_SIZE] = {0};
1,075✔
253

254
  while (numOfRows < rows) {
3,225✔
255
    pShow->pIter = sdbFetch(pSdb, SDB_SECURITY_POLICY, pShow->pIter, (void **)&pObj);
3,225✔
256
    if (pShow->pIter == NULL) break;
3,225✔
257

258
    int32_t cols = 0;
2,150✔
259

260
    if (pObj->type == TSDB_SECURITY_POLICY_SOD) {
2,150✔
261
      int32_t sodPhase = mndGetSoDPhase(pMnode);
1,075✔
262
      bool    sodEnabled = (pObj->status == SOD_MODE_ENABLED);
1,075✔
263

264
      STR_WITH_MAXSIZE_TO_VARSTR(buf, "SoD", pShow->pMeta->pSchemas[cols].bytes);
1,075✔
265
      SColumnInfoData *pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
266
      COL_DATA_SET_VAL_GOTO(buf, false, pObj, pShow->pIter, _OVER);
1,075✔
267

268
      STR_WITH_MAXSIZE_TO_VARSTR(buf, sodEnabled ? "enabled" : _SoDMandatoryInfo[sodPhase][0],
1,075✔
269
                                 pShow->pMeta->pSchemas[cols].bytes);
270
      pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
271
      COL_DATA_SET_VAL_GOTO(buf, false, pObj, pShow->pIter, _OVER);
1,075✔
272

273
      STR_WITH_MAXSIZE_TO_VARSTR(buf, sodEnabled ? "SYSTEM" : pObj->activator, pShow->pMeta->pSchemas[cols].bytes);
1,075✔
274
      pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
275
      COL_DATA_SET_VAL_GOTO(buf, false, pObj, pShow->pIter, _OVER);
1,075✔
276

277
      pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
278
      COL_DATA_SET_VAL_GOTO((const char *)&pObj->updateTime, false, pObj, pShow->pIter, _OVER);
1,075✔
279

280
      STR_WITH_MAXSIZE_TO_VARSTR(buf, sodEnabled ? "non-mandatory, root not disabled" : _SoDMandatoryInfo[sodPhase][1],
1,075✔
281
                                 pShow->pMeta->pSchemas[cols].bytes);
282
      pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
283
      COL_DATA_SET_VAL_GOTO(buf, false, pObj, pShow->pIter, _OVER);
1,075✔
284

285
    } else if (pObj->type == TSDB_SECURITY_POLICY_MAC) {
1,075✔
286
      bool macActive = (pObj->status == MAC_MODE_MANDATORY);
1,075✔
287

288
      STR_WITH_MAXSIZE_TO_VARSTR(buf, "MAC", pShow->pMeta->pSchemas[cols].bytes);
1,075✔
289
      SColumnInfoData *pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
290
      COL_DATA_SET_VAL_GOTO(buf, false, pObj, pShow->pIter, _OVER);
1,075✔
291

292
      STR_WITH_MAXSIZE_TO_VARSTR(buf, macActive ? "mandatory" : "disabled", pShow->pMeta->pSchemas[cols].bytes);
1,075✔
293
      pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
294
      COL_DATA_SET_VAL_GOTO(buf, false, pObj, pShow->pIter, _OVER);
1,075✔
295

296
      STR_WITH_MAXSIZE_TO_VARSTR(buf, macActive ? pObj->activator : "SYSTEM", pShow->pMeta->pSchemas[cols].bytes);
1,075✔
297
      pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
298
      COL_DATA_SET_VAL_GOTO(buf, false, pObj, pShow->pIter, _OVER);
1,075✔
299

300
      pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
301
      COL_DATA_SET_VAL_GOTO((const char *)&pObj->updateTime, false, pObj, pShow->pIter, _OVER);
1,075✔
302

303
      STR_WITH_MAXSIZE_TO_VARSTR(buf, macActive ? "security levels 0-4; activated, irreversible" : "not activated",
1,075✔
304
                                 pShow->pMeta->pSchemas[cols].bytes);
305
      pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,075✔
306
      COL_DATA_SET_VAL_GOTO(buf, false, pObj, pShow->pIter, _OVER);
1,075✔
307

308
    } else {
309
      // Unknown policy type — skip row silently
NEW
310
      sdbRelease(pSdb, pObj);
×
NEW
311
      continue;
×
312
    }
313

314
    sdbRelease(pSdb, pObj);
2,150✔
315
    ++numOfRows;
2,150✔
316
  }
317

318
  pShow->numOfRows += numOfRows;
1,075✔
319

320
_OVER:
1,075✔
321
  if (code != 0) {
1,075✔
NEW
322
    mError("failed to retrieve security policies at line %d since %s", lino, tstrerror(code));
×
NEW
323
    TAOS_RETURN(code);
×
324
  }
325
  return numOfRows;
1,075✔
326
}
327

NEW
328
static void mndCancelGetNextSecurityPolicy(SMnode *pMnode, void *pIter) {
×
NEW
329
  SSdb *pSdb = pMnode->pSdb;
×
NEW
330
  sdbCancelFetchByType(pSdb, pIter, SDB_SECURITY_POLICY);
×
NEW
331
}
×
332

333
// ---- SoD config handler ----
334

335
// #ifdef TD_ENTERPRISE
336
int32_t mndProcessConfigSoDReq(SMnode *pMnode, SRpcMsg *pReq, SMCfgClusterReq *pCfg) {
1,260✔
337
  int32_t code = 0, lino = 0;
1,260✔
338
#ifdef TD_ENTERPRISE
339
  SSecurityPolicyObj obj = {0};
1,260✔
340
  STrans            *pTrans = NULL;
1,260✔
341
  SUserObj          *pRootUser = NULL;
1,260✔
342
  SUserObj           newRootUser = {0};
1,260✔
343

344
  TAOS_CHECK_EXIT(mndCheckOperPrivilege(pMnode, RPC_MSG_USER(pReq), RPC_MSG_TOKEN(pReq), MND_OPER_CONFIG_SOD));
1,260✔
345

346
  if (taosStrncasecmp(pCfg->value, "mandatory", 10) != 0) {
1,080✔
347
    TAOS_CHECK_EXIT(TSDB_CODE_INVALID_CFG_VALUE);
360✔
348
  }
349

350
  SSecurityPolicyObj *pObj = mndAcquireSecPolicy(pMnode, TSDB_SECURITY_POLICY_SOD);
720✔
351
  if (!pObj) {
720✔
NEW
352
    TAOS_CHECK_EXIT(TSDB_CODE_APP_IS_STARTING);
×
353
  }
354

355
  if (pObj->status == SOD_MODE_MANDATORY) {
720✔
NEW
356
    mndReleaseSecPolicy(pMnode, pObj);
×
NEW
357
    TAOS_RETURN(0);
×
358
  }
359

360
  if ((code = mndCheckManagementRoleStatus(pMnode, NULL, 0))) {
720✔
361
    mndReleaseSecPolicy(pMnode, pObj);
540✔
362
    TAOS_CHECK_EXIT(code);
540✔
363
  }
364

365
  mInfo("update security policy SoD mode to mandatory by %s", RPC_MSG_USER(pReq));
180✔
366
  (void)memcpy(&obj, pObj, sizeof(SSecurityPolicyObj));
180✔
367
  obj.status = SOD_MODE_MANDATORY;
180✔
368
  obj.activateTime = taosGetTimestampMs();
180✔
369
  obj.updateTime = obj.activateTime;
180✔
370
  tstrncpy(obj.activator, RPC_MSG_USER(pReq), sizeof(obj.activator));
180✔
371
  mndReleaseSecPolicy(pMnode, pObj);
180✔
372

373
  TAOS_CHECK_EXIT(mndAcquireUser(pMnode, "root", &pRootUser));
180✔
374
  TAOS_CHECK_EXIT(mndUserDupObj(pRootUser, &newRootUser));
180✔
375
  newRootUser.enable = 0;
180✔
376

377
  pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_ROLE, pReq, "update-sod-mode");
180✔
378
  if (pTrans == NULL) {
180✔
NEW
379
    TAOS_CHECK_EXIT(terrno);
×
380
  }
381

382
  SSdbRaw *pCommitRaw = mndSecPolicyActionEncode(&obj);
180✔
383
  if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) {
180✔
NEW
384
    if (pCommitRaw) sdbFreeRaw(pCommitRaw);
×
NEW
385
    TAOS_CHECK_EXIT(terrno);
×
386
  }
387
  TAOS_CHECK_EXIT(sdbSetRawStatus(pCommitRaw, SDB_STATUS_READY));
180✔
388

389
  SSdbRaw *pCommitRawRoot = mndUserActionEncode(&newRootUser);
180✔
390
  if (pCommitRawRoot == NULL || mndTransAppendCommitlog(pTrans, pCommitRawRoot) != 0) {
180✔
NEW
391
    if (pCommitRawRoot) sdbFreeRaw(pCommitRawRoot);
×
NEW
392
    TAOS_CHECK_EXIT(terrno);
×
393
  }
394
  TAOS_CHECK_EXIT(sdbSetRawStatus(pCommitRawRoot, SDB_STATUS_READY));
180✔
395

396
  mndSetSoDPhase(pMnode, TSDB_SOD_PHASE_ENFORCE);
180✔
397
  mndTransSetCb(pTrans, 0, TRANS_STOP_FUNC_SOD, NULL, 0);
180✔
398
  if ((code = mndTransPrepare(pMnode, pTrans)) != 0) {
180✔
NEW
399
    mndSetSoDPhase(pMnode, TSDB_SOD_PHASE_STABLE);
×
NEW
400
    TAOS_CHECK_EXIT(code);
×
401
  }
402

403
_exit:
1,260✔
404
  if (pRootUser) mndReleaseUser(pMnode, pRootUser);
1,260✔
405
  mndUserFreeObj(&newRootUser);
1,260✔
406
  mndTransDrop(pTrans);
1,260✔
407
  if (code < 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
1,260✔
408
    mError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
1,080✔
409
  }
410
#endif
411
  TAOS_RETURN(code);
1,260✔
412
}
413
// #endif
414

415
// ---- MAC config handler ----
416

417
// #ifdef TD_ENTERPRISE
418
int32_t mndProcessConfigMacReq(SMnode *pMnode, SRpcMsg *pReq, SMCfgClusterReq *pCfg) {
2,340✔
419
  int32_t            code = 0, lino = 0;
2,340✔
420
  SSecurityPolicyObj obj = {0};
2,340✔
421
  STrans            *pTrans = NULL;
2,340✔
422
  SUserObj          *pScanUser = NULL;
2,340✔
423
  void              *pScanIter = NULL;
2,340✔
424

425
  TAOS_CHECK_EXIT(mndCheckOperPrivilege(pMnode, RPC_MSG_USER(pReq), RPC_MSG_TOKEN(pReq), MND_OPER_CONFIG_MAC));
2,340✔
426

427
  if (taosStrncasecmp(pCfg->value, "mandatory", 10) != 0) {
1,980✔
428
    TAOS_CHECK_EXIT(TSDB_CODE_INVALID_CFG_VALUE);
360✔
429
  }
430

431
  SSecurityPolicyObj *pObj = mndAcquireSecPolicy(pMnode, TSDB_SECURITY_POLICY_MAC);
1,620✔
432
  if (!pObj) {
1,620✔
NEW
433
    TAOS_CHECK_EXIT(TSDB_CODE_APP_IS_STARTING);
×
434
  }
435

436
  if (pObj->status == MAC_MODE_MANDATORY) {
1,620✔
437
    mInfo("cluster MAC is already mandatory, ignoring repeated activation by %s", RPC_MSG_USER(pReq));
180✔
438
    mndReleaseSecPolicy(pMnode, pObj);
180✔
439
    TAOS_RETURN(0);
180✔
440
  }
441

442
  // Pre-activation check:
443
  // (A) Every user holding any system role (SYSSEC/SYSAUDIT/SYSAUDIT_LOG/SYSDBA) must have
444
  //     both minSecLevel and maxSecLevel satisfying the role's floor constraints:
445
  //       SYSSEC/SYSAUDIT/SYSAUDIT_LOG: min floor=4, max floor=4
446
  //       SYSDBA: min floor=0, max floor=3
447
  // (B) Any user directly holding PRIV_SECURITY_POLICY_ALTER (not via role) must have maxSecLevel=4.
448
  //     (No constraint on minSecLevel for this check.)
449
  // On the first failing user found, immediately abort and return its name in the error message.
450
  {
451
    SSdb *pSdb = pMnode->pSdb;
1,440✔
452

453
    while ((pScanIter = sdbFetch(pSdb, SDB_USER, pScanIter, (void **)&pScanUser)) != NULL) {
2,520✔
454
      if (pScanUser->superUser) {
2,340✔
455
        sdbRelease(pSdb, pScanUser);
180✔
456
        pScanUser = NULL;
180✔
457
        continue;
180✔
458
      }
459
      int8_t floorMaxLevel = mndGetUserRoleFloorMaxLevel(pScanUser->roles);
2,160✔
460
      int8_t floorMinLevel = mndGetUserRoleFloorMinLevel(pScanUser->roles);
2,160✔
461
      bool   hasDirectPriv = PRIV_HAS(&pScanUser->sysPrivs, PRIV_SECURITY_POLICY_ALTER);
2,160✔
462
      // Skip users with neither a system role nor direct PRIV_SECURITY_POLICY_ALTER
463
      if (floorMaxLevel == 0 && floorMinLevel == 0 && !hasDirectPriv) {
2,160✔
NEW
464
        sdbRelease(pSdb, pScanUser);
×
NEW
465
        pScanUser = NULL;
×
NEW
466
        continue;
×
467
      }
468
      char   reason[256] = {0};
2,160✔
469
      int8_t hintMin = 0, hintMax = 0;
2,160✔
470
      if (pScanUser->maxSecLevel < floorMaxLevel) {
2,160✔
471
        snprintf(reason, sizeof(reason), "maxSecLevel(%d) < required maxFloor(%d) (role constraint)",
2,520✔
472
                 (int32_t)pScanUser->maxSecLevel, (int32_t)floorMaxLevel);
1,260✔
473
        hintMin = floorMinLevel;
1,260✔
474
        hintMax = floorMaxLevel;
1,260✔
475
      } else if (pScanUser->minSecLevel < floorMinLevel) {
900✔
NEW
476
        snprintf(reason, sizeof(reason), "minSecLevel(%d) < required minFloor(%d) (role constraint)",
×
NEW
477
                 (int32_t)pScanUser->minSecLevel, (int32_t)floorMinLevel);
×
NEW
478
        hintMin = floorMinLevel;
×
NEW
479
        hintMax = floorMaxLevel;
×
480
      } else if (hasDirectPriv && pScanUser->maxSecLevel < TSDB_MAX_SECURITY_LEVEL) {
900✔
481
        // (B): direct PRIV_SECURITY_POLICY_ALTER holder must have maxSecLevel=4
NEW
482
        snprintf(reason, sizeof(reason),
×
483
                 "maxSecLevel(%d) < %d (direct PRIV_SECURITY_POLICY_ALTER holder must have maxSecLevel=%d)",
NEW
484
                 (int32_t)pScanUser->maxSecLevel, (int32_t)TSDB_MAX_SECURITY_LEVEL, (int32_t)TSDB_MAX_SECURITY_LEVEL);
×
NEW
485
        hintMin = pScanUser->minSecLevel;  // min is already acceptable; keep it
×
NEW
486
        hintMax = TSDB_MAX_SECURITY_LEVEL;
×
487
      }
488
      if (reason[0] != '\0') {
2,160✔
489
        mError("MAC preflight: user '%s' %s", pScanUser->user, reason);
1,260✔
490
        char detail[512];
1,260✔
491
        snprintf(detail, sizeof(detail),
2,520✔
492
                 "Cannot enable MAC: user '%s' %s. "
493
                 "Please ALTER USER %s SECURITY_LEVEL %d,%d to satisfy constraints first.",
494
                 pScanUser->user, reason, pScanUser->user, (int32_t)hintMin, (int32_t)hintMax);
1,260✔
495
        sdbRelease(pSdb, pScanUser);
1,260✔
496
        pScanUser = NULL;
1,260✔
497
        sdbCancelFetch(pSdb, pScanIter);
1,260✔
498
        pScanIter = NULL;
1,260✔
499

500
        int32_t detailLen = strlen(detail) + 1;
1,260✔
501
        void   *pRsp = rpcMallocCont(detailLen);
1,260✔
502
        if (pRsp != NULL) {
1,260✔
503
          memcpy(pRsp, detail, detailLen);
1,260✔
504
          pReq->info.rspLen = detailLen;
1,260✔
505
          pReq->info.rsp = pRsp;
1,260✔
506
        }
507
        mndReleaseSecPolicy(pMnode, pObj);
1,260✔
508
        code = TSDB_CODE_MAC_PRECHECK_FAILED;
1,260✔
509
        goto _exit;
1,260✔
510
      }
511
      sdbRelease(pSdb, pScanUser);
900✔
512
      pScanUser = NULL;
900✔
513
    }
514
    pScanIter = NULL;
180✔
515
  }
516

517
  mInfo("activating cluster MAC by %s", RPC_MSG_USER(pReq));
180✔
518
  (void)memcpy(&obj, pObj, sizeof(SSecurityPolicyObj));
180✔
519
  obj.status = MAC_MODE_MANDATORY;
180✔
520
  obj.activateTime = taosGetTimestampMs();
180✔
521
  obj.updateTime = obj.activateTime;
180✔
522
  tstrncpy(obj.activator, RPC_MSG_USER(pReq), sizeof(obj.activator));
180✔
523
  mndReleaseSecPolicy(pMnode, pObj);
180✔
524
  pObj = NULL;
180✔
525

526
  pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_ROLE, pReq, "activate-mac");
180✔
527
  if (pTrans == NULL) {
180✔
NEW
528
    TAOS_CHECK_EXIT(terrno);
×
529
  }
530

531
  SSdbRaw *pCommitRaw = mndSecPolicyActionEncode(&obj);
180✔
532
  if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) {
180✔
NEW
533
    if (pCommitRaw) sdbFreeRaw(pCommitRaw);
×
NEW
534
    TAOS_CHECK_EXIT(terrno);
×
535
  }
536
  TAOS_CHECK_EXIT(sdbSetRawStatus(pCommitRaw, SDB_STATUS_READY));
180✔
537

538
  if ((code = mndTransPrepare(pMnode, pTrans)) != 0) {
180✔
NEW
539
    TAOS_CHECK_EXIT(code);
×
540
  }
541

542
_exit:
2,160✔
543
  if (pScanIter) sdbCancelFetch(pMnode->pSdb, pScanIter);
2,160✔
544
  if (pScanUser) sdbRelease(pMnode->pSdb, pScanUser);
2,160✔
545
  mndTransDrop(pTrans);
2,160✔
546
  if (code < 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
2,160✔
547
    mError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
1,980✔
548
  }
549
  TAOS_RETURN(code);
2,160✔
550
}
551
// #endif
552

553
// ---- SoD enforcement ----
554

555
void mndSodTransStop(SMnode *pMnode, void *param, int32_t paramLen) {
180✔
556
#ifdef TD_ENTERPRISE
557
  mInfo("SoD trans stop, set SoD phase to %d", TSDB_SOD_PHASE_STABLE);
180✔
558
  mndSetSoDPhase(pMnode, TSDB_SOD_PHASE_STABLE);
180✔
559
#endif
560
}
180✔
561

NEW
562
void mndSodGrantRoleStop(SMnode *pMnode, void *param, int32_t paramLen) {
×
563
#ifdef TD_ENTERPRISE
NEW
564
  if (mndGetSoDPhase(pMnode) != TSDB_SOD_PHASE_INITIAL) {
×
NEW
565
    return;
×
566
  }
567

NEW
568
  if (mndCheckManagementRoleStatus(pMnode, NULL, 0) == 0) {
×
NEW
569
    mInfo("SoD role check completed, all mandatory roles satisfied, trigger enforce SoD");
×
NEW
570
    (void)mndProcessEnforceSodImpl(pMnode);
×
571
  }
572
#endif
573
}
574

575
#ifdef TD_ENTERPRISE
NEW
576
static int32_t mndProcessEnforceSodImpl(SMnode *pMnode) {
×
NEW
577
  int32_t code = 0, lino = 0;
×
NEW
578
  int32_t contLen = 0;
×
NEW
579
  void   *pCont = NULL;
×
580

NEW
581
  SMCfgClusterReq cfgReq = {0};
×
NEW
582
  tsnprintf(cfgReq.config, sizeof(cfgReq.config), "SoD");
×
NEW
583
  tsnprintf(cfgReq.value, sizeof(cfgReq.value), "mandatory");
×
NEW
584
  contLen = tSerializeSMCfgClusterReq(NULL, 0, &cfgReq);
×
NEW
585
  TAOS_CHECK_EXIT(contLen);
×
NEW
586
  if (!(pCont = rpcMallocCont(contLen))) {
×
NEW
587
    TAOS_CHECK_EXIT(TSDB_CODE_OUT_OF_MEMORY);
×
588
  }
589

NEW
590
  if ((code = tSerializeSMCfgClusterReq(pCont, contLen, &cfgReq)) != contLen) {
×
NEW
591
    rpcFreeCont(pCont);
×
NEW
592
    TAOS_CHECK_EXIT(code);
×
593
  }
594

NEW
595
  SRpcMsg rpcMsg = {.pCont = pCont,
×
596
                    .contLen = contLen,
597
                    .msgType = TDMT_MND_CONFIG_CLUSTER,
598
                    .info.ahandle = 0,
599
                    .info.notFreeAhandle = 1};
NEW
600
  SEpSet  epSet = {0};
×
NEW
601
  mndGetMnodeEpSet(pMnode, &epSet);
×
NEW
602
  TAOS_CHECK_EXIT(tmsgSendReq(&epSet, &rpcMsg));
×
NEW
603
_exit:
×
NEW
604
  if (code < 0) {
×
NEW
605
    mError("failed at line %d to enforce SoD since %s", lino, tstrerror(code));
×
606
  }
NEW
607
  TAOS_RETURN(code);
×
608
}
609

NEW
610
int32_t mndProcessEnforceSod(SMnode *pMnode) {
×
NEW
611
  int32_t code = 0, lino = 0;
×
612

NEW
613
  SSecurityPolicyObj *pObj = mndAcquireSecPolicy(pMnode, TSDB_SECURITY_POLICY_SOD);
×
NEW
614
  if (pObj == NULL) {
×
NEW
615
    TAOS_CHECK_EXIT(terrno);
×
616
  }
617

NEW
618
  if (pObj->status == SOD_MODE_MANDATORY) {
×
NEW
619
    mInfo("cluster is already in SoD mandatory mode");
×
NEW
620
    mndReleaseSecPolicy(pMnode, pObj);
×
NEW
621
    TAOS_RETURN(0);
×
622
  }
623

NEW
624
  mndSetSoDPhase(pMnode, TSDB_SOD_PHASE_INITIAL);
×
NEW
625
  mInfo("start to enforce SoD from initial phase, secpolicy type:%d", pObj->type);
×
NEW
626
  if ((code = mndCheckManagementRoleStatus(pMnode, NULL, 0))) {
×
NEW
627
    mndReleaseSecPolicy(pMnode, pObj);
×
NEW
628
    TAOS_CHECK_EXIT(code);
×
629
  }
NEW
630
  mndReleaseSecPolicy(pMnode, pObj);
×
631

NEW
632
  TAOS_CHECK_EXIT(mndProcessEnforceSodImpl(pMnode));
×
NEW
633
  code = TSDB_CODE_ACTION_IN_PROGRESS;
×
634

NEW
635
_exit:
×
NEW
636
  if (code < 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
×
NEW
637
    mError("failed to enforce SoD at line %d since %s", lino, tstrerror(code));
×
638
  }
NEW
639
  TAOS_RETURN(code);
×
640
}
641
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc