From 1379851360349d6617ad32944a25ee5e2bb74fc2 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Sat, 8 Aug 2015 19:12:30 -0400 Subject: Baseline firejail 0.9.28 --- src/lib/Makefile.in | 20 ++ src/lib/common.c | 192 ++++++++++++ src/lib/libnetlink.c | 803 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib/pid.c | 392 +++++++++++++++++++++++++ 4 files changed, 1407 insertions(+) create mode 100644 src/lib/Makefile.in create mode 100644 src/lib/common.c create mode 100644 src/lib/libnetlink.c create mode 100644 src/lib/pid.c (limited to 'src/lib') diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in new file mode 100644 index 000000000..6e6be1910 --- /dev/null +++ b/src/lib/Makefile.in @@ -0,0 +1,20 @@ +PREFIX=@prefix@ +VERSION=@PACKAGE_VERSION@ +NAME=@PACKAGE_NAME@ + +H_FILE_LIST = $(wildcard *.[h]) +C_FILE_LIST = $(wildcard *.c) +OBJS = $(C_FILE_LIST:.c=.o) +BINOBJS = $(foreach file, $(OBJS), $file) +CFLAGS += -ggdb -O2 -DVERSION='"$(VERSION)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIC -Wformat -Wformat-security +LDFLAGS:=-pic -Wl,-z,relro -Wl,-z,now + +all: $(OBJS) + +%.o : %.c $(H_FILE_LIST) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +clean:; rm -f $(OBJS) + +distclean: clean + rm -fr Makefile diff --git a/src/lib/common.c b/src/lib/common.c new file mode 100644 index 000000000..6d928abbb --- /dev/null +++ b/src/lib/common.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * This file is part of firejail project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/common.h" + +int join_namespace(pid_t pid, char *type) { + char *path; + if (asprintf(&path, "/proc/%u/ns/%s", pid, type) == -1) + errExit("asprintf"); + + int fd = open(path, O_RDONLY); + if (fd < 0) { + free(path); + fprintf(stderr, "Error: cannot open /proc/%u/ns/%s.\n", pid, type); + return -1; + } + + if (syscall(__NR_setns, fd, 0) < 0) { + free(path); + fprintf(stderr, "Error: cannot join namespace %s.\n", type); + close(fd); + return -1; + } + + close(fd); + free(path); + return 0; +} + +// return 1 if error +int name2pid(const char *name, pid_t *pid) { + pid_t parent = getpid(); + + DIR *dir; + if (!(dir = opendir("/proc"))) { + // sleep 2 seconds and try again + sleep(2); + if (!(dir = opendir("/proc"))) { + fprintf(stderr, "Error: cannot open /proc directory\n"); + exit(1); + } + } + + struct dirent *entry; + char *end; + while ((entry = readdir(dir))) { + pid_t newpid = strtol(entry->d_name, &end, 10); + if (end == entry->d_name || *end) + continue; + if (newpid == parent) + continue; + + // check if this is a firejail executable + char *comm = pid_proc_comm(newpid); + if (comm) { + // remove \n + char *ptr = strchr(comm, '\n'); + if (ptr) + *ptr = '\0'; + if (strcmp(comm, "firejail")) { + free(comm); + continue; + } + free(comm); + } + + char *cmd = pid_proc_cmdline(newpid); + if (cmd) { + // mark the end of the name + char *ptr = strstr(cmd, "--name="); + char *start = ptr; + if (!ptr) { + free(cmd); + continue; + } + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') + ptr++; + *ptr = '\0'; + int rv = strcmp(start + 7, name); + if (rv == 0) { + free(cmd); + *pid = newpid; + closedir(dir); + return 0; + } + free(cmd); + } + } + closedir(dir); + return 1; +} + +#define BUFLEN 4096 +char *pid_proc_comm(const pid_t pid) { + // open /proc/pid/cmdline file + char *fname; + int fd; + if (asprintf(&fname, "/proc/%d//comm", pid) == -1) + return NULL; + if ((fd = open(fname, O_RDONLY)) < 0) { + free(fname); + return NULL; + } + free(fname); + + // read file + unsigned char buffer[BUFLEN]; + ssize_t len; + if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0) { + close(fd); + return NULL; + } + buffer[len] = '\0'; + close(fd); + + // return a malloc copy of the command line + char *rv = strdup((char *) buffer); + if (strlen(rv) == 0) { + free(rv); + return NULL; + } + return rv; +} + +char *pid_proc_cmdline(const pid_t pid) { + // open /proc/pid/cmdline file + char *fname; + int fd; + if (asprintf(&fname, "/proc/%d/cmdline", pid) == -1) + return NULL; + if ((fd = open(fname, O_RDONLY)) < 0) { + free(fname); + return NULL; + } + free(fname); + + // read file + unsigned char buffer[BUFLEN]; + ssize_t len; + if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0) { + close(fd); + return NULL; + } + buffer[len] = '\0'; + close(fd); + + // clean data + int i; + for (i = 0; i < len; i++) { + if (buffer[i] == '\0') + buffer[i] = ' '; + if (buffer[i] >= 0x80) // execv in progress!!! + return NULL; + } + + // return a malloc copy of the command line + char *rv = strdup((char *) buffer); + if (strlen(rv) == 0) { + free(rv); + return NULL; + } + return rv; +} diff --git a/src/lib/libnetlink.c b/src/lib/libnetlink.c new file mode 100644 index 000000000..264632a01 --- /dev/null +++ b/src/lib/libnetlink.c @@ -0,0 +1,803 @@ +/* file extracted from iproute2 software package + * + * Original source code: + * + * Information: + * http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 + * + * Download: + * http://www.kernel.org/pub/linux/utils/net/iproute2/ + * + * Repository: + * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git + * + * License: GPL v2 + * + * Original copyright header + * + * libnetlink.c RTnetlink service routines. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../include/libnetlink.h" + +int rcvbuf = 1024 * 1024; + +void rtnl_close(struct rtnl_handle *rth) +{ + if (rth->fd >= 0) { + close(rth->fd); + rth->fd = -1; + } +} + +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, + int protocol) +{ + socklen_t addr_len; + int sndbuf = 32768; + + memset(rth, 0, sizeof(*rth)); + + rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); + if (rth->fd < 0) { + perror("Cannot open netlink socket"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + perror("SO_SNDBUF"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) { + perror("SO_RCVBUF"); + return -1; + } + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + rth->local.nl_groups = subscriptions; + + if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { + perror("Cannot bind netlink socket"); + return -1; + } + addr_len = sizeof(rth->local); + if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { + perror("Cannot getsockname"); + return -1; + } + if (addr_len != sizeof(rth->local)) { + fprintf(stderr, "Wrong address length %d\n", addr_len); + return -1; + } + if (rth->local.nl_family != AF_NETLINK) { + fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); + return -1; + } + rth->seq = time(NULL); + return 0; +} + +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +{ + return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); +} + +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +{ + return rtnl_wilddump_req_filter(rth, family, type, RTEXT_FILTER_VF); +} + +int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int family, int type, + __u32 filt_mask) +{ + struct { + struct nlmsghdr nlh; + struct ifinfomsg ifm; + /* attribute has to be NLMSG aligned */ + struct rtattr ext_req __attribute__ ((aligned(NLMSG_ALIGNTO))); + __u32 ext_filter_mask; + } req; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.ifm.ifi_family = family; + + req.ext_req.rta_type = IFLA_EXT_MASK; + req.ext_req.rta_len = RTA_LENGTH(sizeof(__u32)); + req.ext_filter_mask = filt_mask; + + return send(rth->fd, (void*)&req, sizeof(req), 0); +} + +int rtnl_send(struct rtnl_handle *rth, const void *buf, int len) +{ + return send(rth->fd, buf, len, 0); +} + +int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int len) +{ + struct nlmsghdr *h; + int status; + char resp[1024]; + + status = send(rth->fd, buf, len, 0); + if (status < 0) + return status; + + /* Check for immediate errors */ + status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK); + if (status < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + + for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status); + h = NLMSG_NEXT(h, status)) { + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + fprintf(stderr, "ERROR truncated\n"); + else + errno = -err->error; + return -1; + } + } + + return 0; +} + +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +{ + struct nlmsghdr nlh; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + struct iovec iov[2] = { + { .iov_base = &nlh, .iov_len = sizeof(nlh) }, + { .iov_base = req, .iov_len = len } + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = iov, + .msg_iovlen = 2, + }; + + nlh.nlmsg_len = NLMSG_LENGTH(len); + nlh.nlmsg_type = type; + nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; + nlh.nlmsg_pid = 0; + nlh.nlmsg_seq = rth->dump = ++rth->seq; + + return sendmsg(rth->fd, &msg, 0); +} + +int rtnl_dump_filter_l(struct rtnl_handle *rth, + const struct rtnl_dump_filter_arg *arg) +{ + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[16384]; + int dump_intr = 0; + + iov.iov_base = buf; + while (1) { + int status; + const struct rtnl_dump_filter_arg *a; + int found_done = 0; + int msglen = 0; + + iov.iov_len = sizeof(buf); + status = recvmsg(rth->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + return -1; + } + + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + + for (a = arg; a->filter; a++) { + struct nlmsghdr *h = (struct nlmsghdr*)buf; + msglen = status; + + while (NLMSG_OK(h, msglen)) { + int err; + + if (nladdr.nl_pid != 0 || + h->nlmsg_pid != rth->local.nl_pid || + h->nlmsg_seq != rth->dump) + goto skip_it; + + if (h->nlmsg_flags & NLM_F_DUMP_INTR) + dump_intr = 1; + + if (h->nlmsg_type == NLMSG_DONE) { + found_done = 1; + break; /* process next filter */ + } + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, + "ERROR truncated\n"); + } else { + errno = -err->error; + perror("RTNETLINK answers"); + } + return -1; + } + err = a->filter(&nladdr, h, a->arg1); + if (err < 0) + return err; + +skip_it: + h = NLMSG_NEXT(h, msglen); + } + } + + if (found_done) { + if (dump_intr) + fprintf(stderr, + "Dump was interrupted and may be inconsistent.\n"); + return 0; + } + + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (msglen) { + fprintf(stderr, "!!!Remnant of size %d\n", msglen); + exit(1); + } + } +} + +int rtnl_dump_filter(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1) +{ + const struct rtnl_dump_filter_arg a[2] = { + { .filter = filter, .arg1 = arg1, }, + { .filter = NULL, .arg1 = NULL, }, + }; + + return rtnl_dump_filter_l(rth, a); +} + +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer) +{ + int status; + unsigned seq; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov = { + .iov_base = (void*) n, + .iov_len = n->nlmsg_len + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[16384]; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = peer; + nladdr.nl_groups = groups; + + n->nlmsg_seq = seq = ++rtnl->seq; + + if (answer == NULL) + n->nlmsg_flags |= NLM_F_ACK; + + status = sendmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + perror("Cannot talk to rtnetlink"); + return -1; + } + + memset(buf,0,sizeof(buf)); + + iov.iov_base = buf; + + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + return -1; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l < 0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + if (nladdr.nl_pid != peer || + h->nlmsg_pid != rtnl->local.nl_pid || + h->nlmsg_seq != seq) { + /* Don't forget to skip that message. */ + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (l < sizeof(struct nlmsgerr)) { + fprintf(stderr, "ERROR truncated\n"); + } else { + if (!err->error) { + if (answer) + memcpy(answer, h, h->nlmsg_len); + return 0; + } + + fprintf(stderr, "RTNETLINK answers: %s\n", strerror(-err->error)); + errno = -err->error; + } + return -1; + } + if (answer) { + memcpy(answer, h, h->nlmsg_len); + return 0; + } + + fprintf(stderr, "Unexpected reply!!!\n"); + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_listen(struct rtnl_handle *rtnl, + rtnl_filter_t handler, + void *jarg) +{ + int status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[8192]; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + iov.iov_base = buf; + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + if (errno == ENOBUFS) + continue; + return -1; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler, + void *jarg) +{ + int status; + struct sockaddr_nl nladdr; + char buf[8192]; + struct nlmsghdr *h = (void*)buf; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + while (1) { + int err, len; + int l; + + status = fread(&buf, 1, sizeof(*h), rtnl); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("rtnl_from_file: fread"); + return -1; + } + if (status == 0) + return 0; + + len = h->nlmsg_len; + l = len - sizeof(*h); + + if (l<0 || len>sizeof(buf)) { + fprintf(stderr, "!!!malformed message: len=%d @%lu\n", + len, ftell(rtnl)); + return -1; + } + + status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl); + + if (status < 0) { + perror("rtnl_from_file: fread"); + return -1; + } + if (status < l) { + fprintf(stderr, "rtnl-from_file: truncated message\n"); + return -1; + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + } +} + +int addattr(struct nlmsghdr *n, int maxlen, int type) +{ + return addattr_l(n, maxlen, type, NULL, 0); +} + +int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u8)); +} + +int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u16)); +} + +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u32)); +} + +int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u64)); +} + +int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *str) +{ + return addattr_l(n, maxlen, type, str, strlen(str)+1); +} + + + +int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) +{ + +#if 0 +printf("%d: %s\n", __LINE__, __FUNCTION__); +printf("\ttype %d - ", type); +if (type == IFLA_LINK) { + printf("IFLA_LINK\n"); + int i; + printf("\tdata - "); + for (i = 0; i < alen; i++) + printf("%02x, ", *((unsigned char *)data + i)); + printf("\n"); +} +else if (type == IFLA_IFNAME) { + printf("IFLA_IFNAME\n"); + printf("\tdata - #%s#\n", data); +} +else if (type == IFLA_LINKINFO) printf("IFLA_LINKINFO\n"); +else if (type == IFLA_ADDRESS) { + printf("IFLA_ADDRESS or IFLA_INFO_KIND\n"); + int i; + printf("\tdata - "); + for (i = 0; i < alen; i++) + printf("%02x, ", *((unsigned char *)data + i)); + printf("\n"); +} +else if (type == IFLA_BROADCAST) printf("IFLA_BROADCAST or IFLA_INFO_DATA\n"); + +printf("\tdata length: %d\n", alen); +#endif + + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + return 0; +} + +#if 0 +int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) +{ +printf("%s: adding type %d, length %d ", __FUNCTION__, type, alen); +if (type == IFLA_INFO_KIND) { +if (alen) + printf("(IFLA_INFO_KIND %s)\n", (char *)data); +else +printf("(VETH_INFO_PEER)\n"); +} +else if (type == IFLA_IFNAME) { +printf("(IFLA_IFNAME %s)\n", (char *) data); +} +else if (type == IFLA_NET_NS_PID) { +printf("(IFLA_NET_NS_PID %u)\n", *((unsigned *) data)); +} +else if (type == IFLA_LINKINFO) +printf("(IFLA_LINKINFO)\n"); +else if (type == IFLA_INFO_DATA) +printf("(IFLA_INFO_DATA)\n"); +else + printf("\n"); + + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + return 0; +} +#endif + +int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len) +{ + if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) { + fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + + memcpy(NLMSG_TAIL(n), data, len); + memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len); + return 0; +} + +struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) +{ + struct rtattr *nest = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, NULL, 0); + return nest; +} + +int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) +{ + nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; + return n->nlmsg_len; +} + +struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, + const void *data, int len) +{ + struct rtattr *start = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, data, len); + addattr_nest(n, maxlen, type); + return start; +} + +int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start) +{ + struct rtattr *nest = (void *)start + NLMSG_ALIGN(start->rta_len); + + start->rta_len = (void *)NLMSG_TAIL(n) - (void *)start; + addattr_nest_end(n, nest); + return n->nlmsg_len; +} + +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *subrta; + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), &data, 4); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, + const void *data, int alen) +{ + struct rtattr *subrta; + int len = RTA_LENGTH(alen); + + if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), data, alen); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len); + return 0; +} + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + return parse_rtattr_flags(tb, max, rta, len, 0); +} + +int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, + int len, unsigned short flags) +{ + unsigned short type; + + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + type = rta->rta_type & ~flags; + if ((type <= max) && (!tb[type])) + tb[type] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return 0; +} + +int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + int i = 0; + + memset(tb, 0, sizeof(struct rtattr *) * max); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max && i < max) + tb[i++] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return i; +} + +int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, + int len) +{ + if (RTA_PAYLOAD(rta) < len) + return -1; + if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) { + rta = RTA_DATA(rta) + RTA_ALIGN(len); + return parse_rtattr_nested(tb, max, rta); + } + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + return 0; +} diff --git a/src/lib/pid.c b/src/lib/pid.c new file mode 100644 index 000000000..a0261ead2 --- /dev/null +++ b/src/lib/pid.c @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * This file is part of firejail project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "../include/common.h" +#include "../include/pid.h" +#include +#include +#include +#include +#include + +#define PIDS_BUFLEN 4096 +//Process pids[max_pids]; +Process *pids = NULL; +int max_pids=32769; +#define PIDS_BUFLEN 4096 + +// get the memory associated with this pid +void pid_getmem(unsigned pid, unsigned *rss, unsigned *shared) { + // open stat file + char *file; + if (asprintf(&file, "/proc/%u/statm", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) { + free(file); + return; + } + free(file); + + unsigned a, b, c; + if (3 != fscanf(fp, "%u %u %u", &a, &b, &c)) { + fclose(fp); + return; + } + *rss += b; + *shared += c; + fclose(fp); +} + + +void pid_get_cpu_time(unsigned pid, unsigned *utime, unsigned *stime) { + // open stat file + char *file; + if (asprintf(&file, "/proc/%u/stat", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) { + free(file); + return; + } + free(file); + + char line[PIDS_BUFLEN]; + if (fgets(line, PIDS_BUFLEN - 1, fp)) { + char *ptr = line; + // jump 13 fields + int i; + for (i = 0; i < 13; i++) { + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') + ptr++; + if (*ptr == '\0') + goto myexit; + ptr++; + } + if (2 != sscanf(ptr, "%u %u", utime, stime)) + goto myexit; + } + +myexit: + fclose(fp); +} + +unsigned long long pid_get_start_time(unsigned pid) { + // open stat file + char *file; + if (asprintf(&file, "/proc/%u/stat", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) { + free(file); + return 0; + } + free(file); + + char line[PIDS_BUFLEN]; + unsigned long long retval = 0; + if (fgets(line, PIDS_BUFLEN - 1, fp)) { + char *ptr = line; + // jump 21 fields + int i; + for (i = 0; i < 21; i++) { + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') + ptr++; + if (*ptr == '\0') + goto myexit; + ptr++; + } + if (1 != sscanf(ptr, "%llu", &retval)) + goto myexit; + } + +myexit: + fclose(fp); + return retval; +} + +char *pid_get_user_name(uid_t uid) { + struct passwd *pw = getpwuid(uid); + if (pw) + return strdup(pw->pw_name); + return NULL; +} + +uid_t pid_get_uid(pid_t pid) { + uid_t rv = 0; + + // open stat file + char *file; + if (asprintf(&file, "/proc/%u/status", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) { + free(file); + return 0; + } + + // look for firejail executable name + char buf[PIDS_BUFLEN]; + while (fgets(buf, PIDS_BUFLEN - 1, fp)) { + if (strncmp(buf, "Uid:", 4) == 0) { + char *ptr = buf + 5; + while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { + ptr++; + } + if (*ptr == '\0') + goto doexit; + + rv = atoi(ptr); + break; // break regardless! + } + } +doexit: + fclose(fp); + free(file); + return rv; +} + +static void print_elem(unsigned index, int nowrap) { + // get terminal size + struct winsize sz; + int col = 0; + if (isatty(STDIN_FILENO)) { + if (!ioctl(0, TIOCGWINSZ, &sz)) + col = sz.ws_col; + } + + // indent + char indent[(pids[index].level - 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent)); + indent[(pids[index].level - 1) * 2] = '\0'; + + // get data + uid_t uid = pids[index].uid; + char *cmd = pid_proc_cmdline(index); + char *user = pid_get_user_name(uid); + char *allocated = user; + if (user ==NULL) + user = ""; + if (cmd) { + if (col < 4 || nowrap) + printf("%s%u:%s:%s\n", indent, index, user, cmd); + else { + char *out; + if (asprintf(&out, "%s%u:%s:%s\n", indent, index, user, cmd) == -1) + errExit("asprintf"); + int len = strlen(out); + if (len > col) { + out[col] = '\0'; + out[col - 1] = '\n'; + } + printf("%s", out); + free(out); + } + + free(cmd); + } + else { + if (pids[index].zombie) + printf("%s%u: (zombie)\n", indent, index); + else + printf("%s%u:\n", indent, index); + } + if (allocated) + free(allocated); +} + +// recursivity!!! +void pid_print_tree(unsigned index, unsigned parent, int nowrap) { + print_elem(index, nowrap); + + int i; + for (i = index + 1; i < max_pids; i++) { + if (pids[i].parent == index) + pid_print_tree(i, index, nowrap); + } + + for (i = 0; i < index; i++) { + if (pids[i].parent == index) + pid_print_tree(i, index, nowrap); + } +} + +void pid_print_list(unsigned index, int nowrap) { + print_elem(index, nowrap); +} + +// recursivity!!! +void pid_store_cpu(unsigned index, unsigned parent, unsigned *utime, unsigned *stime) { + if (pids[index].level == 1) { + *utime = 0; + *stime = 0; + } + + unsigned utmp = 0; + unsigned stmp = 0; + pid_get_cpu_time(index, &utmp, &stmp); + *utime += utmp; + *stime += stmp; + + int i; + for (i = index + 1; i < max_pids; i++) { + if (pids[i].parent == index) + pid_store_cpu(i, index, utime, stime); + } + + if (pids[index].level == 1) { + pids[index].utime = *utime; + pids[index].stime = *stime; + } +} + +// mon_pid: pid of sandbox to be monitored, 0 if all sandboxes are included +void pid_read(pid_t mon_pid) { + if (pids == NULL) { + FILE *fp = fopen("/proc/sys/kernel/pid_max", "r"); + if (fp) { + int val; + if (fscanf(fp, "%d", &val) == 1) { + if (val >= max_pids) + max_pids = val + 1; + } + fclose(fp); + } + pids = malloc(sizeof(Process) * max_pids); + if (pids == NULL) + errExit("malloc"); + } + memset(pids, 0, sizeof(Process) * max_pids); + pid_t mypid = getpid(); + + DIR *dir; + if (!(dir = opendir("/proc"))) { + // sleep 2 seconds and try again + sleep(2); + if (!(dir = opendir("/proc"))) { + fprintf(stderr, "Error: cannot open /proc directory\n"); + exit(1); + } + } + + pid_t child = -1; + struct dirent *entry; + char *end; + while (child < 0 && (entry = readdir(dir))) { + pid_t pid = strtol(entry->d_name, &end, 10); + pid %= max_pids; + if (end == entry->d_name || *end) + continue; + if (pid == mypid) + continue; + + // open stat file + char *file; + if (asprintf(&file, "/proc/%u/status", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) { + free(file); + continue; + } + + // look for firejail executable name + char buf[PIDS_BUFLEN]; + while (fgets(buf, PIDS_BUFLEN - 1, fp)) { + if (strncmp(buf, "Name:", 5) == 0) { + char *ptr = buf + 5; + while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { + ptr++; + } + if (*ptr == '\0') { + fprintf(stderr, "Error: cannot read /proc file\n"); + exit(1); + } + + if (mon_pid == 0 && strncmp(ptr, "firejail", 8) == 0) { + pids[pid].level = 1; + } + else if (mon_pid == pid && strncmp(ptr, "firejail", 8) == 0) { + pids[pid].level = 1; + } +// else if (mon_pid == 0 && strncmp(ptr, "lxc-execute", 11) == 0) { +// pids[pid].level = 1; +// } +// else if (mon_pid == pid && strncmp(ptr, "lxc-execute", 11) == 0) { +// pids[pid].level = 1; +// } + else + pids[pid].level = -1; + } + if (strncmp(buf, "State:", 6) == 0) { + if (strstr(buf, "(zombie)")) + pids[pid].zombie = 1; + } + else if (strncmp(buf, "PPid:", 5) == 0) { + char *ptr = buf + 5; + while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { + ptr++; + } + if (*ptr == '\0') { + fprintf(stderr, "Error: cannot read /proc file\n"); + exit(1); + } + unsigned parent = atoi(ptr); + parent %= max_pids; + if (pids[parent].level > 0) { + pids[pid].level = pids[parent].level + 1; + } + pids[pid].parent = parent; + } + else if (strncmp(buf, "Uid:", 4) == 0) { + char *ptr = buf + 5; + while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { + ptr++; + } + if (*ptr == '\0') { + fprintf(stderr, "Error: cannot read /proc file\n"); + exit(1); + } + pids[pid].uid = atoi(ptr); + break; + } + } + fclose(fp); + free(file); + } + closedir(dir); + + pid_t pid; + for (pid = 0; pid < max_pids; pid++) { + int parent = pids[pid].parent; + if (pids[parent].level > 0) { + pids[pid].level = pids[parent].level + 1; + } + } +} -- cgit v1.2.3-70-g09d2