/* Example code using libcap to set CAP_NET_BIND_SERVICE (binding to port 81) to the effective capability set. For more info, see the full blog post at https://k3a.me/linux-capabilities-in-a-nutshell/ */ #include #include #include #include /* === from : #define _LINUX_CAPABILITY_VERSION_1 0x19980330 #define _LINUX_CAPABILITY_U32S_1 1 #define _LINUX_CAPABILITY_VERSION_2 0x20071026 // deprecated - use v3 #define _LINUX_CAPABILITY_U32S_2 2 #define _LINUX_CAPABILITY_VERSION_3 0x20080522 #define _LINUX_CAPABILITY_U32S_3 2 typedef struct __user_cap_header_struct { __u32 version; int pid; } __user *cap_user_header_t; typedef struct __user_cap_data_struct { __u32 effective; __u32 permitted; __u32 inheritable; } __user *cap_user_data_t; */ /* === from libcap/libcap.h struct _cap_struct { struct __user_cap_header_struct head; struct __user_cap_data_struct set; }; */ #define handle_error(msg) \ do \ { \ perror(msg); \ exit(EXIT_FAILURE); \ } while (0) void hex_dump(const void *data, size_t size) { char ascii[17]; size_t i, j; ascii[16] = '\0'; for (i = 0; i < size; ++i) { printf("%02X ", ((unsigned char *)data)[i]); if (((unsigned char *)data)[i] >= ' ' && ((unsigned char *)data)[i] <= '~') { ascii[i % 16] = ((unsigned char *)data)[i]; } else { ascii[i % 16] = '.'; } if ((i + 1) % 8 == 0 || i + 1 == size) { printf(" "); if ((i + 1) % 16 == 0) { printf("| %s \n", ascii); } else if (i + 1 == size) { ascii[(i + 1) % 16] = '\0'; if ((i + 1) % 16 <= 8) { printf(" "); } for (j = (i + 1) % 16; j < 16; ++j) { printf(" "); } printf("| %s \n", ascii); } } } } #include #include #include static void test_bind_socket() { int sockfd, newsockfd, portno = 81, clilen; char buffer[256]; struct sockaddr_in serv_addr, cli_addr; int n; /* First call to socket() function */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) handle_error("socket"); /* Initialize socket structure */ bzero((char *)&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(portno); /* Now bind the host address using bind() call.*/ if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) handle_error("bind"); else printf("bind successful! :)\n"); } static void dump_caps(cap_t caps) { unsigned capsz = sizeof(struct __user_cap_header_struct); cap_user_header_t caphead = (cap_user_header_t)caps; if (caphead->version == _LINUX_CAPABILITY_VERSION_1) capsz += sizeof(struct __user_cap_data_struct); else capsz += 2 * sizeof(struct __user_cap_data_struct); hex_dump(caps, capsz); } int main(int argc, char *argv[]) { cap_t caps; char *txt_caps; // get caps currently set to the process caps = cap_get_proc(); if (caps == NULL) handle_error("cap_get_proc"); // dump caps as binary binary dump_caps(caps); // convert capability to text representation txt_caps = cap_to_text(caps, NULL); if (txt_caps == NULL) handle_error("cap_to_text"); printf("Current process capabilities (+set): %s\n", txt_caps); // free the allocated capability text memory if (cap_free(txt_caps) != 0) handle_error("cap_free"); // set NET_BIND_SERVICE to effective set // (requires NET_BIND_SERVICE already in the permitted set, // otherwise fails with EPERM. One of the ways to set permitted // set is by setting file capabilities: // sudo setcap cap_net_bind_service+p ./captest // ) cap_value_t cap_list[1]; cap_list[0] = CAP_NET_BIND_SERVICE; if (cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET) == -1) handle_error("cap_set_flag"); if (cap_set_proc(caps) != 0) handle_error("cap_set_proc"); // try to bind privileged port 81 test_bind_socket(); // free the allocated caps if (cap_free(caps) == -1) handle_error("cap_free"); exit(EXIT_SUCCESS); }