Skip to content

Instantly share code, notes, and snippets.

@k3a
Created April 25, 2019 16:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save k3a/a38f4439f85eda4f77eda19c4ae8c80c to your computer and use it in GitHub Desktop.
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.
/*
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);
}
@yvv4git
Copy link

yvv4git commented Dec 1, 2020

how it build?

@k3a
Copy link
Author

k3a commented Dec 1, 2020

gcc -lcap captest.c -o captest

See the full article https://k3a.me/linux-capabilities-in-a-nutshell/

@yvv4git
Copy link

yvv4git commented Dec 2, 2020

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment