1
#if !defined(__linux__)
2
#error "Linux platform file is part of non-Linux build."
3
#endif
4

            
5
#include "starter/privileged_service_protocol.h"
6

            
7
#include <asm-generic/socket.h>
8
#include <bits/types/struct_iovec.h>
9
#include <cerrno>
10
#include <cstdint>
11
#include <cstring>
12
#include <linux/capability.h>
13
#include <stdio.h>	// NOLINT
14
#include <stdlib.h>	// NOLINT
15
#include <sys/socket.h>
16
#include <sys/syscall.h>
17
#include <sys/unistd.h>	// NOLINT
18
#include <unistd.h>	// NOLINT
19

            
20
namespace Envoy {
21
namespace Cilium {
22
namespace PrivilegedService {
23

            
24
// Capabiilty names used in DumpCapabilities responses.
25
static const char* cap_names[64] = {
26
    "CAP_CHOWN",              //  0
27
    "CAP_DAC_OVERRIDE",       //  1
28
    "CAP_DAC_READ_SEARCH",    //  2
29
    "CAP_FOWNER",             //  3
30
    "CAP_FSETID",             //  4
31
    "CAP_KILL",               //  5
32
    "CAP_SETGID",             //  6
33
    "CAP_SETUID",             //  7
34
    "CAP_SETPCAP",            //  8
35
    "CAP_LINUX_IMMUTABLE",    //  9
36
    "CAP_NET_BIND_SERVICE",   // 10
37
    "CAP_NET_BROADCAST",      // 11
38
    "CAP_NET_ADMIN",          // 12
39
    "CAP_NET_RAW",            // 13
40
    "CAP_IPC_LOCK",           // 14
41
    "CAP_IPC_OWNER",          // 15
42
    "CAP_SYS_MODULE",         // 16
43
    "CAP_SYS_RAWIO",          // 17
44
    "CAP_SYS_CHROOT",         // 18
45
    "CAP_SYS_PTRACE",         // 19
46
    "CAP_SYS_PACCT",          // 20
47
    "CAP_SYS_ADMIN",          // 21
48
    "CAP_SYS_BOOT",           // 22
49
    "CAP_SYS_NICE",           // 23
50
    "CAP_SYS_RESOURCE",       // 24
51
    "CAP_SYS_TIME",           // 25
52
    "CAP_SYS_TTY_CONFIG",     // 26
53
    "CAP_MKNOD",              // 27
54
    "CAP_LEASE",              // 28
55
    "CAP_AUDIT_WRITE",        // 29
56
    "CAP_AUDIT_CONTROL",      // 30
57
    "CAP_SETFCAP",            // 31
58
    "CAP_MAC_OVERRIDE",       // 32
59
    "CAP_MAC_ADMIN",          // 33
60
    "CAP_SYSLOG",             // 34
61
    "CAP_WAKE_ALARM",         // 35
62
    "CAP_BLOCK_SUSPEND",      // 36
63
    "CAP_AUDIT_READ",         // 37
64
    "CAP_PERFMON",            // 38
65
    "CAP_BPF",                // 39
66
    "CAP_CHECKPOINT_RESTORE", // 40
67
    "CAP_41",
68
    "CAP_42",
69
    "CAP_43",
70
    "CAP_44",
71
    "CAP_45",
72
    "CAP_46",
73
    "CAP_47",
74
    "CAP_48",
75
    "CAP_49",
76
    "CAP_50",
77
    "CAP_51",
78
    "CAP_52",
79
    "CAP_53",
80
    "CAP_54",
81
    "CAP_55",
82
    "CAP_56",
83
    "CAP_57",
84
    "CAP_58",
85
    "CAP_59",
86
    "CAP_60",
87
    "CAP_61",
88
    "CAP_62",
89
    "CAP_63",
90
};
91

            
92
// Get a 64-bit set of capabilities of the given kind
93
uint64_t getCapabilities(cap_flag_t kind) {
94
  struct __user_cap_header_struct hdr{_LINUX_CAPABILITY_VERSION_3, 0};
95
  struct __user_cap_data_struct data[2];
96
  memset(&data, 0, sizeof(data));
97
  int rc = ::syscall(SYS_capget, &hdr, &data, sizeof(data));
98
  if (rc != 0) {
99
    perror("capget");
100
    exit(1);
101
  }
102

            
103
  if (kind == CAP_INHERITABLE) {
104
    return data[0].inheritable | uint64_t(data[1].inheritable) << 32;
105
  }
106
  if (kind == CAP_PERMITTED) {
107
    return data[0].permitted | uint64_t(data[1].permitted) << 32;
108
  }
109
  if (kind == CAP_EFFECTIVE) {
110
    return data[0].effective | uint64_t(data[1].effective) << 32;
111
  }
112
  fprintf(stderr, "getCapabilities: invalid kind: %d\n", kind);
113
  ::abort();
114
  return 0;
115
}
116

            
117
// dumpCaps returns the capabilities of the given kind in string form.
118
size_t dumpCapabilities(cap_flag_t kind, char* buf, size_t buf_size) {
119
  size_t remaining = buf_size;
120
  uint64_t caps = getCapabilities(kind);
121

            
122
  auto append = [&](const char* str) {
123
    auto len = strlen(str);
124
    if (len < remaining) {
125
      memcpy(buf, str, len);
126
      remaining -= len;
127
      buf += len;
128
    }
129
  };
130

            
131
  for (int i = 0, n = 0; i < 64; i++) {
132
    if (caps & (1UL << i)) {
133
      if (n > 0) {
134
        append(", ");
135
      }
136
      append(cap_names[i]);
137
      n++;
138
    }
139
  }
140

            
141
  return buf_size - remaining;
142
}
143

            
144
Protocol::~Protocol() { close(); }
145

            
146
Protocol::Protocol(int fd) : fd_(fd) {}
147

            
148
void Protocol::close() {
149
  if (fd_ != -1) {
150
    ::close(fd_);
151
  }
152
  fd_ = -1;
153
}
154

            
155
namespace {
156

            
157
static inline struct msghdr initIov(struct iovec iov[2], const void* header, ssize_t headerlen,
158
                                    const void* data, ssize_t datalen) {
159
  struct msghdr msg{};
160
  msg.msg_iov = iov;
161
  msg.msg_iovlen = 1;
162
  iov[0].iov_base = const_cast<void*>(header);
163
  iov[0].iov_len = headerlen;
164
  if (data && datalen > 0) {
165
    msg.msg_iovlen = 2;
166
    iov[1].iov_base = const_cast<void*>(data);
167
    iov[1].iov_len = datalen;
168
  }
169
  return msg;
170
}
171

            
172
} // namespace
173

            
174
ssize_t Protocol::sendFdMsg(const void* header, ssize_t headerlen, const void* data,
175
                              ssize_t datalen, int fd) {
176
  struct iovec iov[2];
177
  struct msghdr msg = initIov(iov, header, headerlen, data, datalen);
178
  union {
179
    struct cmsghdr cmsghdr;
180
    char control[CMSG_SPACE(sizeof(int))];
181
  } cmsgu;
182
  struct cmsghdr* cmsg;
183

            
184
  // set up msg control for an fd?
185
  if (fd != -1) {
186
    msg.msg_control = cmsgu.control;
187
    msg.msg_controllen = sizeof(cmsgu.control);
188
    cmsg = CMSG_FIRSTHDR(&msg);
189
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
190
    cmsg->cmsg_level = SOL_SOCKET;
191
    cmsg->cmsg_type = SCM_RIGHTS;
192
    *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd;
193
  }
194

            
195
  // send the request
196
  ssize_t size;
197
  do {
198
    size = sendmsg(fd_, &msg, 0);
199
  } while (size < 0 && errno == EINTR);
200

            
201
  if (size >= 0 && size != headerlen + datalen) {
202
    fprintf(stderr, "sendmsg truncated (%zd < %zd)\n", size, headerlen + datalen);
203
  }
204
  return size;
205
}
206

            
207
ssize_t Protocol::recvFdMsg(const void* header, ssize_t headersize, const void* data,
208
                              ssize_t datasize, int* fd) {
209
  struct iovec iov[2];
210
  struct msghdr msg = initIov(iov, header, headersize, data, datasize);
211
  union {
212
    struct cmsghdr cmsghdr;
213
    char control[CMSG_SPACE(sizeof(int))];
214
  } cmsgu;
215
  msg.msg_control = cmsgu.control;
216
  msg.msg_controllen = sizeof(cmsgu.control);
217

            
218
  ssize_t size;
219
  do {
220
    size = recvmsg(fd_, &msg, 0);
221
  } while (size < 0 && errno == EINTR);
222

            
223
  if (size >= 0 && fd) {
224
    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
225
    if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int)) && cmsg->cmsg_level == SOL_SOCKET &&
226
        cmsg->cmsg_type == SCM_RIGHTS) {
227
      *fd = *reinterpret_cast<int*>(CMSG_DATA(cmsg));
228
    } else {
229
      *fd = -1;
230
    }
231
  }
232
  return size;
233
}
234

            
235
} // namespace PrivilegedService
236
} // namespace Cilium
237
} // namespace Envoy