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

taosdata / TDengine / #5032

24 Apr 2026 11:25AM UTC coverage: 73.076% (+0.2%) from 72.876%
#5032

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%)

527 existing lines in 138 files now uncovered.

275965 of 377640 relevant lines covered (73.08%)

132797765.0 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) {
484,069✔
40
  SSdbTable table = {
484,069✔
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);
484,069✔
52
  mndAddShowFreeIterHandle(pMnode, TSDB_MGMT_TABLE_SECURITY_POLICIES, mndCancelGetNextSecurityPolicy);
484,069✔
53

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

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

60
void mndCleanupSecurityPolicy(SMnode *pMnode) {}
484,005✔
61

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

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

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

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

82
  terrno = 0;
2,558,778✔
83

84
_OVER:
2,558,778✔
85
  if (terrno != 0) {
2,558,778✔
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,558,778✔
92
  return pRaw;
2,558,778✔
93
}
94

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

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

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

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

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

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

125
  terrno = 0;
968,448✔
126

127
_OVER:
968,448✔
128
  if (terrno != 0) {
968,448✔
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);
968,448✔
135
  return pRow;
968,448✔
136
}
137

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

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

152
static int32_t mndSecPolicyActionUpdate(SSdb *pSdb, SSecurityPolicyObj *pOld, SSecurityPolicyObj *pNew) {
342✔
153
  mTrace("secpolicy:%d, perform update action, old row:%p new row:%p", pOld->type, pOld, pNew);
342✔
154
  pOld->updateTime = pNew->updateTime;
342✔
155
  pOld->activateTime = pNew->activateTime;
342✔
156
  pOld->status = pNew->status;
342✔
157
  tstrncpy(pOld->activator, pNew->activator, sizeof(pOld->activator));
342✔
158
  (void)memcpy(pOld->reserve, pNew->reserve, sizeof(pOld->reserve));
342✔
159
  // Sync MAC state cache: fires when Raft commit-log is applied — the true success point
160
  if (pOld->type == TSDB_SECURITY_POLICY_MAC) {
342✔
161
    pSdb->pMnode->macActive = pOld->status;
171✔
162
  }
163
  return 0;
342✔
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) {
699,758✔
170
  SSecurityPolicyObj obj = {0};
699,758✔
171
  obj.type = policyType;
699,758✔
172
  obj.createdTime = taosGetTimestampMs();
699,758✔
173
  obj.updateTime = obj.createdTime;
699,758✔
174
  obj.activateTime = obj.createdTime;
699,758✔
175
  // status defaults to 0 (SEC_POLICY_STATUS_DEFAULT) for both SOD and MAC
176

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

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

186
  code = mndTransAppendCommitlog(pTrans, pRaw);
699,758✔
187
  if (code != 0) {
699,758✔
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);
699,758✔
195
}
196

197
static int32_t mndCreateDefaultSecurityPolicy(SMnode *pMnode) {
349,879✔
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");
349,879✔
201
  if (pTrans == NULL) return terrno;
349,879✔
202

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

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

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

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

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

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

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

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

228
int32_t mndGetClusterSoDMode(SMnode *pMnode) {
61,042✔
229
  int32_t             sodMode = SOD_MODE_ENABLED;
61,042✔
230
  SSecurityPolicyObj *pObj = mndAcquireSecPolicy(pMnode, TSDB_SECURITY_POLICY_SOD);
61,042✔
231
  if (pObj != NULL) {
61,042✔
232
    sodMode = pObj->status;
61,042✔
233
    mndReleaseSecPolicy(pMnode, pObj);
61,042✔
234
  }
235
  return sodMode;
61,042✔
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,021✔
247
  SMnode             *pMnode = pMsg->info.node;
1,021✔
248
  SSdb               *pSdb = pMnode->pSdb;
1,021✔
249
  int32_t             code = 0, lino = 0;
1,021✔
250
  int32_t             numOfRows = 0;
1,021✔
251
  SSecurityPolicyObj *pObj = NULL;
1,021✔
252
  char                buf[128 + VARSTR_HEADER_SIZE] = {0};
1,021✔
253

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

258
    int32_t cols = 0;
2,042✔
259

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

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

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

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

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

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

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

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

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

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

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

303
      STR_WITH_MAXSIZE_TO_VARSTR(buf, macActive ? "security levels 0-4; activated, irreversible" : "not activated",
1,021✔
304
                                 pShow->pMeta->pSchemas[cols].bytes);
305
      pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
1,021✔
306
      COL_DATA_SET_VAL_GOTO(buf, false, pObj, pShow->pIter, _OVER);
1,021✔
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,042✔
315
    ++numOfRows;
2,042✔
316
  }
317

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

320
_OVER:
1,021✔
321
  if (code != 0) {
1,021✔
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,021✔
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,197✔
337
  int32_t code = 0, lino = 0;
1,197✔
338
#ifdef TD_ENTERPRISE
339
  SSecurityPolicyObj obj = {0};
1,197✔
340
  STrans            *pTrans = NULL;
1,197✔
341
  SUserObj          *pRootUser = NULL;
1,197✔
342
  SUserObj           newRootUser = {0};
1,197✔
343

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

436
  if (pObj->status == MAC_MODE_MANDATORY) {
1,539✔
437
    mInfo("cluster MAC is already mandatory, ignoring repeated activation by %s", RPC_MSG_USER(pReq));
171✔
438
    mndReleaseSecPolicy(pMnode, pObj);
171✔
439
    TAOS_RETURN(0);
171✔
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,368✔
452

453
    while ((pScanIter = sdbFetch(pSdb, SDB_USER, pScanIter, (void **)&pScanUser)) != NULL) {
2,394✔
454
      if (pScanUser->superUser) {
2,223✔
455
        sdbRelease(pSdb, pScanUser);
171✔
456
        pScanUser = NULL;
171✔
457
        continue;
171✔
458
      }
459
      int8_t floorMaxLevel = mndGetUserRoleFloorMaxLevel(pScanUser->roles);
2,052✔
460
      int8_t floorMinLevel = mndGetUserRoleFloorMinLevel(pScanUser->roles);
2,052✔
461
      bool   hasDirectPriv = PRIV_HAS(&pScanUser->sysPrivs, PRIV_SECURITY_POLICY_ALTER);
2,052✔
462
      // Skip users with neither a system role nor direct PRIV_SECURITY_POLICY_ALTER
463
      if (floorMaxLevel == 0 && floorMinLevel == 0 && !hasDirectPriv) {
2,052✔
NEW
464
        sdbRelease(pSdb, pScanUser);
×
NEW
465
        pScanUser = NULL;
×
NEW
466
        continue;
×
467
      }
468
      char   reason[256] = {0};
2,052✔
469
      int8_t hintMin = 0, hintMax = 0;
2,052✔
470
      if (pScanUser->maxSecLevel < floorMaxLevel) {
2,052✔
471
        snprintf(reason, sizeof(reason), "maxSecLevel(%d) < required maxFloor(%d) (role constraint)",
2,394✔
472
                 (int32_t)pScanUser->maxSecLevel, (int32_t)floorMaxLevel);
1,197✔
473
        hintMin = floorMinLevel;
1,197✔
474
        hintMax = floorMaxLevel;
1,197✔
475
      } else if (pScanUser->minSecLevel < floorMinLevel) {
855✔
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) {
855✔
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,052✔
489
        mError("MAC preflight: user '%s' %s", pScanUser->user, reason);
1,197✔
490
        char detail[512];
1,197✔
491
        snprintf(detail, sizeof(detail),
2,394✔
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,197✔
495
        sdbRelease(pSdb, pScanUser);
1,197✔
496
        pScanUser = NULL;
1,197✔
497
        sdbCancelFetch(pSdb, pScanIter);
1,197✔
498
        pScanIter = NULL;
1,197✔
499

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

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

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

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

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

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

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

555
void mndSodTransStop(SMnode *pMnode, void *param, int32_t paramLen) {
171✔
556
#ifdef TD_ENTERPRISE
557
  mInfo("SoD trans stop, set SoD phase to %d", TSDB_SOD_PHASE_STABLE);
171✔
558
  mndSetSoDPhase(pMnode, TSDB_SOD_PHASE_STABLE);
171✔
559
#endif
560
}
171✔
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