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