You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

label.c 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959
  1. /* -------------------------------------------------------------------------
  2. *
  3. * contrib/sepgsql/label.c
  4. *
  5. * Routines to support SELinux labels (security context)
  6. *
  7. * Copyright (c) 2010-2019, PostgreSQL Global Development Group
  8. *
  9. * -------------------------------------------------------------------------
  10. */
  11. #include "postgres.h"
  12. #include <selinux/label.h>
  13. #include "access/heapam.h"
  14. #include "access/htup_details.h"
  15. #include "access/genam.h"
  16. #include "access/xact.h"
  17. #include "catalog/catalog.h"
  18. #include "catalog/dependency.h"
  19. #include "catalog/indexing.h"
  20. #include "catalog/pg_attribute.h"
  21. #include "catalog/pg_class.h"
  22. #include "catalog/pg_database.h"
  23. #include "catalog/pg_namespace.h"
  24. #include "catalog/pg_proc.h"
  25. #include "commands/dbcommands.h"
  26. #include "commands/seclabel.h"
  27. #include "libpq/auth.h"
  28. #include "libpq/libpq-be.h"
  29. #include "miscadmin.h"
  30. #include "utils/builtins.h"
  31. #include "utils/fmgroids.h"
  32. #include "utils/guc.h"
  33. #include "utils/lsyscache.h"
  34. #include "utils/memutils.h"
  35. #include "utils/rel.h"
  36. #include "utils/tqual.h"
  37. #include "sepgsql.h"
  38. /*
  39. * Saved hook entries (if stacked)
  40. */
  41. static ClientAuthentication_hook_type next_client_auth_hook = NULL;
  42. static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
  43. static fmgr_hook_type next_fmgr_hook = NULL;
  44. /*
  45. * client_label_*
  46. *
  47. * security label of the database client. Initially the client security label
  48. * is equal to client_label_peer, and can be changed by one or more calls to
  49. * sepgsql_setcon(), and also be temporarily overridden during execution of a
  50. * trusted-procedure.
  51. *
  52. * sepgsql_setcon() is a transaction-aware operation; a (sub-)transaction
  53. * rollback should also rollback the current client security label. Therefore
  54. * we use the list client_label_pending of pending_label to keep track of which
  55. * labels were set during the (sub-)transactions.
  56. */
  57. static char *client_label_peer = NULL; /* set by getpeercon(3) */
  58. static List *client_label_pending = NIL; /* pending list being set by
  59. * sepgsql_setcon() */
  60. static char *client_label_committed = NULL; /* set by sepgsql_setcon(), and
  61. * already committed */
  62. static char *client_label_func = NULL; /* set by trusted procedure */
  63. typedef struct
  64. {
  65. SubTransactionId subid;
  66. char *label;
  67. } pending_label;
  68. /*
  69. * sepgsql_get_client_label
  70. *
  71. * Returns the current security label of the client. All code should use this
  72. * routine to get the current label, instead of referring to the client_label_*
  73. * variables above.
  74. */
  75. char *
  76. sepgsql_get_client_label(void)
  77. {
  78. /* trusted procedure client label override */
  79. if (client_label_func)
  80. return client_label_func;
  81. /* uncommitted sepgsql_setcon() value */
  82. if (client_label_pending)
  83. {
  84. pending_label *plabel = llast(client_label_pending);
  85. if (plabel->label)
  86. return plabel->label;
  87. }
  88. else if (client_label_committed)
  89. return client_label_committed; /* set by sepgsql_setcon() committed */
  90. /* default label */
  91. Assert(client_label_peer != NULL);
  92. return client_label_peer;
  93. }
  94. /*
  95. * sepgsql_set_client_label
  96. *
  97. * This routine tries to switch the current security label of the client, and
  98. * checks related permissions. The supplied new label shall be added to the
  99. * client_label_pending list, then saved at transaction-commit time to ensure
  100. * transaction-awareness.
  101. */
  102. static void
  103. sepgsql_set_client_label(const char *new_label)
  104. {
  105. const char *tcontext;
  106. MemoryContext oldcxt;
  107. pending_label *plabel;
  108. /* Reset to the initial client label, if NULL */
  109. if (!new_label)
  110. tcontext = client_label_peer;
  111. else
  112. {
  113. if (security_check_context_raw((security_context_t) new_label) < 0)
  114. ereport(ERROR,
  115. (errcode(ERRCODE_INVALID_NAME),
  116. errmsg("SELinux: invalid security label: \"%s\"",
  117. new_label)));
  118. tcontext = new_label;
  119. }
  120. /* Check process:{setcurrent} permission. */
  121. sepgsql_avc_check_perms_label(sepgsql_get_client_label(),
  122. SEPG_CLASS_PROCESS,
  123. SEPG_PROCESS__SETCURRENT,
  124. NULL,
  125. true);
  126. /* Check process:{dyntransition} permission. */
  127. sepgsql_avc_check_perms_label(tcontext,
  128. SEPG_CLASS_PROCESS,
  129. SEPG_PROCESS__DYNTRANSITION,
  130. NULL,
  131. true);
  132. /*
  133. * Append the supplied new_label on the pending list until the current
  134. * transaction is committed.
  135. */
  136. oldcxt = MemoryContextSwitchTo(CurTransactionContext);
  137. plabel = palloc0(sizeof(pending_label));
  138. plabel->subid = GetCurrentSubTransactionId();
  139. if (new_label)
  140. plabel->label = pstrdup(new_label);
  141. client_label_pending = lappend(client_label_pending, plabel);
  142. MemoryContextSwitchTo(oldcxt);
  143. }
  144. /*
  145. * sepgsql_xact_callback
  146. *
  147. * A callback routine of transaction commit/abort/prepare. Commit or abort
  148. * changes in the client_label_pending list.
  149. */
  150. static void
  151. sepgsql_xact_callback(XactEvent event, void *arg)
  152. {
  153. if (event == XACT_EVENT_COMMIT)
  154. {
  155. if (client_label_pending != NIL)
  156. {
  157. pending_label *plabel = llast(client_label_pending);
  158. char *new_label;
  159. if (plabel->label)
  160. new_label = MemoryContextStrdup(TopMemoryContext,
  161. plabel->label);
  162. else
  163. new_label = NULL;
  164. if (client_label_committed)
  165. pfree(client_label_committed);
  166. client_label_committed = new_label;
  167. /*
  168. * XXX - Note that items of client_label_pending are allocated on
  169. * CurTransactionContext, thus, all acquired memory region shall
  170. * be released implicitly.
  171. */
  172. client_label_pending = NIL;
  173. }
  174. }
  175. else if (event == XACT_EVENT_ABORT)
  176. client_label_pending = NIL;
  177. }
  178. /*
  179. * sepgsql_subxact_callback
  180. *
  181. * A callback routine of sub-transaction start/abort/commit. Releases all
  182. * security labels that are set within the sub-transaction that is aborted.
  183. */
  184. static void
  185. sepgsql_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
  186. SubTransactionId parentSubid, void *arg)
  187. {
  188. ListCell *cell;
  189. ListCell *prev;
  190. ListCell *next;
  191. if (event == SUBXACT_EVENT_ABORT_SUB)
  192. {
  193. prev = NULL;
  194. for (cell = list_head(client_label_pending); cell; cell = next)
  195. {
  196. pending_label *plabel = lfirst(cell);
  197. next = lnext(cell);
  198. if (plabel->subid == mySubid)
  199. client_label_pending
  200. = list_delete_cell(client_label_pending, cell, prev);
  201. else
  202. prev = cell;
  203. }
  204. }
  205. }
  206. /*
  207. * sepgsql_client_auth
  208. *
  209. * Entrypoint of the client authentication hook.
  210. * It switches the client label according to getpeercon(), and the current
  211. * performing mode according to the GUC setting.
  212. */
  213. static void
  214. sepgsql_client_auth(Port *port, int status)
  215. {
  216. if (next_client_auth_hook)
  217. (*next_client_auth_hook) (port, status);
  218. /*
  219. * In the case when authentication failed, the supplied socket shall be
  220. * closed soon, so we don't need to do anything here.
  221. */
  222. if (status != STATUS_OK)
  223. return;
  224. /*
  225. * Getting security label of the peer process using API of libselinux.
  226. */
  227. if (getpeercon_raw(port->sock, &client_label_peer) < 0)
  228. ereport(FATAL,
  229. (errcode(ERRCODE_INTERNAL_ERROR),
  230. errmsg("SELinux: unable to get peer label: %m")));
  231. /*
  232. * Switch the current performing mode from INTERNAL to either DEFAULT or
  233. * PERMISSIVE.
  234. */
  235. if (sepgsql_get_permissive())
  236. sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
  237. else
  238. sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
  239. }
  240. /*
  241. * sepgsql_needs_fmgr_hook
  242. *
  243. * It informs the core whether the supplied function is trusted procedure,
  244. * or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
  245. * abort time of function invocation.
  246. */
  247. static bool
  248. sepgsql_needs_fmgr_hook(Oid functionId)
  249. {
  250. ObjectAddress object;
  251. if (next_needs_fmgr_hook &&
  252. (*next_needs_fmgr_hook) (functionId))
  253. return true;
  254. /*
  255. * SELinux needs the function to be called via security_definer wrapper,
  256. * if this invocation will take a domain-transition. We call these
  257. * functions as trusted-procedure, if the security policy has a rule that
  258. * switches security label of the client on execution.
  259. */
  260. if (sepgsql_avc_trusted_proc(functionId) != NULL)
  261. return true;
  262. /*
  263. * Even if not a trusted-procedure, this function should not be inlined
  264. * unless the client has db_procedure:{execute} permission. Please note
  265. * that it shall be actually failed later because of same reason with
  266. * ACL_EXECUTE.
  267. */
  268. object.classId = ProcedureRelationId;
  269. object.objectId = functionId;
  270. object.objectSubId = 0;
  271. if (!sepgsql_avc_check_perms(&object,
  272. SEPG_CLASS_DB_PROCEDURE,
  273. SEPG_DB_PROCEDURE__EXECUTE |
  274. SEPG_DB_PROCEDURE__ENTRYPOINT,
  275. SEPGSQL_AVC_NOAUDIT, false))
  276. return true;
  277. return false;
  278. }
  279. /*
  280. * sepgsql_fmgr_hook
  281. *
  282. * It switches security label of the client on execution of trusted
  283. * procedures.
  284. */
  285. static void
  286. sepgsql_fmgr_hook(FmgrHookEventType event,
  287. FmgrInfo *flinfo, Datum *private)
  288. {
  289. struct
  290. {
  291. char *old_label;
  292. char *new_label;
  293. Datum next_private;
  294. } *stack;
  295. switch (event)
  296. {
  297. case FHET_START:
  298. stack = (void *) DatumGetPointer(*private);
  299. if (!stack)
  300. {
  301. MemoryContext oldcxt;
  302. oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
  303. stack = palloc(sizeof(*stack));
  304. stack->old_label = NULL;
  305. stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
  306. stack->next_private = 0;
  307. MemoryContextSwitchTo(oldcxt);
  308. /*
  309. * process:transition permission between old and new label,
  310. * when user tries to switch security label of the client on
  311. * execution of trusted procedure.
  312. *
  313. * Also, db_procedure:entrypoint permission should be checked
  314. * whether this procedure can perform as an entrypoint of the
  315. * trusted procedure, or not. Note that db_procedure:execute
  316. * permission shall be checked individually.
  317. */
  318. if (stack->new_label)
  319. {
  320. ObjectAddress object;
  321. object.classId = ProcedureRelationId;
  322. object.objectId = flinfo->fn_oid;
  323. object.objectSubId = 0;
  324. sepgsql_avc_check_perms(&object,
  325. SEPG_CLASS_DB_PROCEDURE,
  326. SEPG_DB_PROCEDURE__ENTRYPOINT,
  327. getObjectDescription(&object),
  328. true);
  329. sepgsql_avc_check_perms_label(stack->new_label,
  330. SEPG_CLASS_PROCESS,
  331. SEPG_PROCESS__TRANSITION,
  332. NULL, true);
  333. }
  334. *private = PointerGetDatum(stack);
  335. }
  336. Assert(!stack->old_label);
  337. if (stack->new_label)
  338. {
  339. stack->old_label = client_label_func;
  340. client_label_func = stack->new_label;
  341. }
  342. if (next_fmgr_hook)
  343. (*next_fmgr_hook) (event, flinfo, &stack->next_private);
  344. break;
  345. case FHET_END:
  346. case FHET_ABORT:
  347. stack = (void *) DatumGetPointer(*private);
  348. if (next_fmgr_hook)
  349. (*next_fmgr_hook) (event, flinfo, &stack->next_private);
  350. if (stack->new_label)
  351. {
  352. client_label_func = stack->old_label;
  353. stack->old_label = NULL;
  354. }
  355. break;
  356. default:
  357. elog(ERROR, "unexpected event type: %d", (int) event);
  358. break;
  359. }
  360. }
  361. /*
  362. * sepgsql_init_client_label
  363. *
  364. * Initializes the client security label and sets up related hooks for client
  365. * label management.
  366. */
  367. void
  368. sepgsql_init_client_label(void)
  369. {
  370. /*
  371. * Set up dummy client label.
  372. *
  373. * XXX - note that PostgreSQL launches background worker process like
  374. * autovacuum without authentication steps. So, we initialize sepgsql_mode
  375. * with SEPGSQL_MODE_INTERNAL, and client_label with the security context
  376. * of server process. Later, it also launches background of user session.
  377. * In this case, the process is always hooked on post-authentication, and
  378. * we can initialize the sepgsql_mode and client_label correctly.
  379. */
  380. if (getcon_raw(&client_label_peer) < 0)
  381. ereport(ERROR,
  382. (errcode(ERRCODE_INTERNAL_ERROR),
  383. errmsg("SELinux: failed to get server security label: %m")));
  384. /* Client authentication hook */
  385. next_client_auth_hook = ClientAuthentication_hook;
  386. ClientAuthentication_hook = sepgsql_client_auth;
  387. /* Trusted procedure hooks */
  388. next_needs_fmgr_hook = needs_fmgr_hook;
  389. needs_fmgr_hook = sepgsql_needs_fmgr_hook;
  390. next_fmgr_hook = fmgr_hook;
  391. fmgr_hook = sepgsql_fmgr_hook;
  392. /* Transaction/Sub-transaction callbacks */
  393. RegisterXactCallback(sepgsql_xact_callback, NULL);
  394. RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
  395. }
  396. /*
  397. * sepgsql_get_label
  398. *
  399. * It returns a security context of the specified database object.
  400. * If unlabeled or incorrectly labeled, the system "unlabeled" label
  401. * shall be returned.
  402. */
  403. char *
  404. sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
  405. {
  406. ObjectAddress object;
  407. char *label;
  408. object.classId = classId;
  409. object.objectId = objectId;
  410. object.objectSubId = subId;
  411. label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
  412. if (!label || security_check_context_raw((security_context_t) label))
  413. {
  414. security_context_t unlabeled;
  415. if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
  416. ereport(ERROR,
  417. (errcode(ERRCODE_INTERNAL_ERROR),
  418. errmsg("SELinux: failed to get initial security label: %m")));
  419. PG_TRY();
  420. {
  421. label = pstrdup(unlabeled);
  422. }
  423. PG_CATCH();
  424. {
  425. freecon(unlabeled);
  426. PG_RE_THROW();
  427. }
  428. PG_END_TRY();
  429. freecon(unlabeled);
  430. }
  431. return label;
  432. }
  433. /*
  434. * sepgsql_object_relabel
  435. *
  436. * An entrypoint of SECURITY LABEL statement
  437. */
  438. void
  439. sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
  440. {
  441. /*
  442. * validate format of the supplied security label, if it is security
  443. * context of selinux.
  444. */
  445. if (seclabel &&
  446. security_check_context_raw((security_context_t) seclabel) < 0)
  447. ereport(ERROR,
  448. (errcode(ERRCODE_INVALID_NAME),
  449. errmsg("SELinux: invalid security label: \"%s\"", seclabel)));
  450. /*
  451. * Do actual permission checks for each object classes
  452. */
  453. switch (object->classId)
  454. {
  455. case DatabaseRelationId:
  456. sepgsql_database_relabel(object->objectId, seclabel);
  457. break;
  458. case NamespaceRelationId:
  459. sepgsql_schema_relabel(object->objectId, seclabel);
  460. break;
  461. case RelationRelationId:
  462. if (object->objectSubId == 0)
  463. sepgsql_relation_relabel(object->objectId,
  464. seclabel);
  465. else
  466. sepgsql_attribute_relabel(object->objectId,
  467. object->objectSubId,
  468. seclabel);
  469. break;
  470. case ProcedureRelationId:
  471. sepgsql_proc_relabel(object->objectId, seclabel);
  472. break;
  473. default:
  474. ereport(ERROR,
  475. (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  476. errmsg("sepgsql provider does not support labels on %s",
  477. getObjectTypeDescription(object))));
  478. break;
  479. }
  480. }
  481. /*
  482. * TEXT sepgsql_getcon(VOID)
  483. *
  484. * It returns the security label of the client.
  485. */
  486. PG_FUNCTION_INFO_V1(sepgsql_getcon);
  487. Datum
  488. sepgsql_getcon(PG_FUNCTION_ARGS)
  489. {
  490. char *client_label;
  491. if (!sepgsql_is_enabled())
  492. PG_RETURN_NULL();
  493. client_label = sepgsql_get_client_label();
  494. PG_RETURN_TEXT_P(cstring_to_text(client_label));
  495. }
  496. /*
  497. * BOOL sepgsql_setcon(TEXT)
  498. *
  499. * It switches the security label of the client.
  500. */
  501. PG_FUNCTION_INFO_V1(sepgsql_setcon);
  502. Datum
  503. sepgsql_setcon(PG_FUNCTION_ARGS)
  504. {
  505. const char *new_label;
  506. if (PG_ARGISNULL(0))
  507. new_label = NULL;
  508. else
  509. new_label = TextDatumGetCString(PG_GETARG_DATUM(0));
  510. sepgsql_set_client_label(new_label);
  511. PG_RETURN_BOOL(true);
  512. }
  513. /*
  514. * TEXT sepgsql_mcstrans_in(TEXT)
  515. *
  516. * It translate the given qualified MLS/MCS range into raw format
  517. * when mcstrans daemon is working.
  518. */
  519. PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
  520. Datum
  521. sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
  522. {
  523. text *label = PG_GETARG_TEXT_PP(0);
  524. char *raw_label;
  525. char *result;
  526. if (!sepgsql_is_enabled())
  527. ereport(ERROR,
  528. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
  529. errmsg("sepgsql is not enabled")));
  530. if (selinux_trans_to_raw_context(text_to_cstring(label),
  531. &raw_label) < 0)
  532. ereport(ERROR,
  533. (errcode(ERRCODE_INTERNAL_ERROR),
  534. errmsg("SELinux: could not translate security label: %m")));
  535. PG_TRY();
  536. {
  537. result = pstrdup(raw_label);
  538. }
  539. PG_CATCH();
  540. {
  541. freecon(raw_label);
  542. PG_RE_THROW();
  543. }
  544. PG_END_TRY();
  545. freecon(raw_label);
  546. PG_RETURN_TEXT_P(cstring_to_text(result));
  547. }
  548. /*
  549. * TEXT sepgsql_mcstrans_out(TEXT)
  550. *
  551. * It translate the given raw MLS/MCS range into qualified format
  552. * when mcstrans daemon is working.
  553. */
  554. PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
  555. Datum
  556. sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
  557. {
  558. text *label = PG_GETARG_TEXT_PP(0);
  559. char *qual_label;
  560. char *result;
  561. if (!sepgsql_is_enabled())
  562. ereport(ERROR,
  563. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
  564. errmsg("sepgsql is not currently enabled")));
  565. if (selinux_raw_to_trans_context(text_to_cstring(label),
  566. &qual_label) < 0)
  567. ereport(ERROR,
  568. (errcode(ERRCODE_INTERNAL_ERROR),
  569. errmsg("SELinux: could not translate security label: %m")));
  570. PG_TRY();
  571. {
  572. result = pstrdup(qual_label);
  573. }
  574. PG_CATCH();
  575. {
  576. freecon(qual_label);
  577. PG_RE_THROW();
  578. }
  579. PG_END_TRY();
  580. freecon(qual_label);
  581. PG_RETURN_TEXT_P(cstring_to_text(result));
  582. }
  583. /*
  584. * quote_object_names
  585. *
  586. * It tries to quote the supplied identifiers
  587. */
  588. static char *
  589. quote_object_name(const char *src1, const char *src2,
  590. const char *src3, const char *src4)
  591. {
  592. StringInfoData result;
  593. const char *temp;
  594. initStringInfo(&result);
  595. if (src1)
  596. {
  597. temp = quote_identifier(src1);
  598. appendStringInfo(&result, "%s", temp);
  599. if (src1 != temp)
  600. pfree((void *) temp);
  601. }
  602. if (src2)
  603. {
  604. temp = quote_identifier(src2);
  605. appendStringInfo(&result, ".%s", temp);
  606. if (src2 != temp)
  607. pfree((void *) temp);
  608. }
  609. if (src3)
  610. {
  611. temp = quote_identifier(src3);
  612. appendStringInfo(&result, ".%s", temp);
  613. if (src3 != temp)
  614. pfree((void *) temp);
  615. }
  616. if (src4)
  617. {
  618. temp = quote_identifier(src4);
  619. appendStringInfo(&result, ".%s", temp);
  620. if (src4 != temp)
  621. pfree((void *) temp);
  622. }
  623. return result.data;
  624. }
  625. /*
  626. * exec_object_restorecon
  627. *
  628. * This routine is a helper called by sepgsql_restorecon; it set up
  629. * initial security labels of database objects within the supplied
  630. * catalog OID.
  631. */
  632. static void
  633. exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
  634. {
  635. Relation rel;
  636. SysScanDesc sscan;
  637. HeapTuple tuple;
  638. char *database_name = get_database_name(MyDatabaseId);
  639. char *namespace_name;
  640. Oid namespace_id;
  641. char *relation_name;
  642. /*
  643. * Open the target catalog. We don't want to allow writable accesses by
  644. * other session during initial labeling.
  645. */
  646. rel = heap_open(catalogId, AccessShareLock);
  647. sscan = systable_beginscan(rel, InvalidOid, false,
  648. NULL, 0, NULL);
  649. while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
  650. {
  651. Form_pg_database datForm;
  652. Form_pg_namespace nspForm;
  653. Form_pg_class relForm;
  654. Form_pg_attribute attForm;
  655. Form_pg_proc proForm;
  656. char *objname;
  657. int objtype = 1234;
  658. ObjectAddress object;
  659. security_context_t context;
  660. /*
  661. * The way to determine object name depends on object classes. So, any
  662. * branches set up `objtype', `objname' and `object' here.
  663. */
  664. switch (catalogId)
  665. {
  666. case DatabaseRelationId:
  667. datForm = (Form_pg_database) GETSTRUCT(tuple);
  668. objtype = SELABEL_DB_DATABASE;
  669. objname = quote_object_name(NameStr(datForm->datname),
  670. NULL, NULL, NULL);
  671. object.classId = DatabaseRelationId;
  672. object.objectId = datForm->oid;
  673. object.objectSubId = 0;
  674. break;
  675. case NamespaceRelationId:
  676. nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
  677. objtype = SELABEL_DB_SCHEMA;
  678. objname = quote_object_name(database_name,
  679. NameStr(nspForm->nspname),
  680. NULL, NULL);
  681. object.classId = NamespaceRelationId;
  682. object.objectId = nspForm->oid;
  683. object.objectSubId = 0;
  684. break;
  685. case RelationRelationId:
  686. relForm = (Form_pg_class) GETSTRUCT(tuple);
  687. if (relForm->relkind == RELKIND_RELATION ||
  688. relForm->relkind == RELKIND_PARTITIONED_TABLE)
  689. objtype = SELABEL_DB_TABLE;
  690. else if (relForm->relkind == RELKIND_SEQUENCE)
  691. objtype = SELABEL_DB_SEQUENCE;
  692. else if (relForm->relkind == RELKIND_VIEW)
  693. objtype = SELABEL_DB_VIEW;
  694. else
  695. continue; /* no need to assign security label */
  696. namespace_name = get_namespace_name(relForm->relnamespace);
  697. objname = quote_object_name(database_name,
  698. namespace_name,
  699. NameStr(relForm->relname),
  700. NULL);
  701. pfree(namespace_name);
  702. object.classId = RelationRelationId;
  703. object.objectId = relForm->oid;
  704. object.objectSubId = 0;
  705. break;
  706. case AttributeRelationId:
  707. attForm = (Form_pg_attribute) GETSTRUCT(tuple);
  708. if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION &&
  709. get_rel_relkind(attForm->attrelid) != RELKIND_PARTITIONED_TABLE)
  710. continue; /* no need to assign security label */
  711. objtype = SELABEL_DB_COLUMN;
  712. namespace_id = get_rel_namespace(attForm->attrelid);
  713. namespace_name = get_namespace_name(namespace_id);
  714. relation_name = get_rel_name(attForm->attrelid);
  715. objname = quote_object_name(database_name,
  716. namespace_name,
  717. relation_name,
  718. NameStr(attForm->attname));
  719. pfree(namespace_name);
  720. pfree(relation_name);
  721. object.classId = RelationRelationId;
  722. object.objectId = attForm->attrelid;
  723. object.objectSubId = attForm->attnum;
  724. break;
  725. case ProcedureRelationId:
  726. proForm = (Form_pg_proc) GETSTRUCT(tuple);
  727. objtype = SELABEL_DB_PROCEDURE;
  728. namespace_name = get_namespace_name(proForm->pronamespace);
  729. objname = quote_object_name(database_name,
  730. namespace_name,
  731. NameStr(proForm->proname),
  732. NULL);
  733. pfree(namespace_name);
  734. object.classId = ProcedureRelationId;
  735. object.objectId = proForm->oid;
  736. object.objectSubId = 0;
  737. break;
  738. default:
  739. elog(ERROR, "unexpected catalog id: %u", catalogId);
  740. objname = NULL; /* for compiler quiet */
  741. break;
  742. }
  743. if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
  744. {
  745. PG_TRY();
  746. {
  747. /*
  748. * Check SELinux permission to relabel the fetched object,
  749. * then do the actual relabeling.
  750. */
  751. sepgsql_object_relabel(&object, context);
  752. SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
  753. }
  754. PG_CATCH();
  755. {
  756. freecon(context);
  757. PG_RE_THROW();
  758. }
  759. PG_END_TRY();
  760. freecon(context);
  761. }
  762. else if (errno == ENOENT)
  763. ereport(WARNING,
  764. (errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
  765. objname, objtype)));
  766. else
  767. ereport(ERROR,
  768. (errcode(ERRCODE_INTERNAL_ERROR),
  769. errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
  770. pfree(objname);
  771. }
  772. systable_endscan(sscan);
  773. heap_close(rel, NoLock);
  774. }
  775. /*
  776. * BOOL sepgsql_restorecon(TEXT specfile)
  777. *
  778. * This function tries to assign initial security labels on all the object
  779. * within the current database, according to the system setting.
  780. * It is typically invoked by sepgsql-install script just after initdb, to
  781. * assign initial security labels.
  782. *
  783. * If @specfile is not NULL, it uses explicitly specified specfile, instead
  784. * of the system default.
  785. */
  786. PG_FUNCTION_INFO_V1(sepgsql_restorecon);
  787. Datum
  788. sepgsql_restorecon(PG_FUNCTION_ARGS)
  789. {
  790. struct selabel_handle *sehnd;
  791. struct selinux_opt seopts;
  792. /*
  793. * SELinux has to be enabled on the running platform.
  794. */
  795. if (!sepgsql_is_enabled())
  796. ereport(ERROR,
  797. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
  798. errmsg("sepgsql is not currently enabled")));
  799. /*
  800. * Check DAC permission. Only superuser can set up initial security
  801. * labels, like root-user in filesystems
  802. */
  803. if (!superuser())
  804. ereport(ERROR,
  805. (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  806. errmsg("SELinux: must be superuser to restore initial contexts")));
  807. /*
  808. * Open selabel_lookup(3) stuff. It provides a set of mapping between an
  809. * initial security label and object class/name due to the system setting.
  810. */
  811. if (PG_ARGISNULL(0))
  812. {
  813. seopts.type = SELABEL_OPT_UNUSED;
  814. seopts.value = NULL;
  815. }
  816. else
  817. {
  818. seopts.type = SELABEL_OPT_PATH;
  819. seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
  820. }
  821. sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
  822. if (!sehnd)
  823. ereport(ERROR,
  824. (errcode(ERRCODE_INTERNAL_ERROR),
  825. errmsg("SELinux: failed to initialize labeling handle: %m")));
  826. PG_TRY();
  827. {
  828. exec_object_restorecon(sehnd, DatabaseRelationId);
  829. exec_object_restorecon(sehnd, NamespaceRelationId);
  830. exec_object_restorecon(sehnd, RelationRelationId);
  831. exec_object_restorecon(sehnd, AttributeRelationId);
  832. exec_object_restorecon(sehnd, ProcedureRelationId);
  833. }
  834. PG_CATCH();
  835. {
  836. selabel_close(sehnd);
  837. PG_RE_THROW();
  838. }
  839. PG_END_TRY();
  840. selabel_close(sehnd);
  841. PG_RETURN_BOOL(true);
  842. }