Created
April 25, 2019 16:28
-
-
Save k3a/a38f4439f85eda4f77eda19c4ae8c80c to your computer and use it in GitHub Desktop.
Example code using libcap to set CAP_NET_BIND_SERVICE (binding to port 81) to the effective capability set.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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 <stdlib.h> | |
#include <stdio.h> | |
#include <sys/capability.h> | |
#include <linux/capability.h> | |
/* === from <linux/capability.h>: | |
#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 <strings.h> | |
#include <netdb.h> | |
#include <netinet/in.h> | |
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); | |
} |
gcc -lcap captest.c -o captest
See the full article https://k3a.me/linux-capabilities-in-a-nutshell/
Thanks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
how it build?