Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016 Fabien Siron <fabien.siron@epita.fr>
3 : : * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
4 : : * Copyright (c) 2016-2017 The strace developers.
5 : : * All rights reserved.
6 : : *
7 : : * Redistribution and use in source and binary forms, with or without
8 : : * modification, are permitted provided that the following conditions
9 : : * are met:
10 : : * 1. Redistributions of source code must retain the above copyright
11 : : * notice, this list of conditions and the following disclaimer.
12 : : * 2. Redistributions in binary form must reproduce the above copyright
13 : : * notice, this list of conditions and the following disclaimer in the
14 : : * documentation and/or other materials provided with the distribution.
15 : : * 3. The name of the author may not be used to endorse or promote products
16 : : * derived from this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 : : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 : : */
29 : :
30 : : #include "defs.h"
31 : : #include "netlink.h"
32 : : #include "nlattr.h"
33 : : #include <linux/audit.h>
34 : : #include <linux/rtnetlink.h>
35 : : #include <linux/xfrm.h>
36 : : #include "xlat/netlink_ack_flags.h"
37 : : #include "xlat/netlink_flags.h"
38 : : #include "xlat/netlink_get_flags.h"
39 : : #include "xlat/netlink_new_flags.h"
40 : : #include "xlat/netlink_protocols.h"
41 : : #include "xlat/netlink_types.h"
42 : : #include "xlat/nl_audit_types.h"
43 : : #include "xlat/nl_crypto_types.h"
44 : : #include "xlat/nl_netfilter_msg_types.h"
45 : : #include "xlat/nl_netfilter_subsys_ids.h"
46 : : #include "xlat/nl_route_types.h"
47 : : #include "xlat/nl_selinux_types.h"
48 : : #include "xlat/nl_sock_diag_types.h"
49 : : #include "xlat/nl_xfrm_types.h"
50 : : #include "xlat/nlmsgerr_attrs.h"
51 : :
52 : : /*
53 : : * Fetch a struct nlmsghdr from the given address.
54 : : */
55 : : static bool
56 : 472 : fetch_nlmsghdr(struct tcb *const tcp, struct nlmsghdr *const nlmsghdr,
57 : : const kernel_ulong_t addr, const kernel_ulong_t len)
58 : : {
59 [ + + ]: 472 : if (len < sizeof(struct nlmsghdr)) {
60 : 10 : printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
61 : 10 : return false;
62 : : }
63 : :
64 [ + + ]: 462 : if (umove_or_printaddr(tcp, addr, nlmsghdr))
65 : : return false;
66 : :
67 : 456 : return true;
68 : : }
69 : :
70 : : enum {
71 : : NL_FAMILY_ERROR = -1,
72 : : NL_FAMILY_DEFAULT = -2
73 : : };
74 : :
75 : : static int
76 : 346 : get_fd_nl_family(struct tcb *const tcp, const int fd)
77 : : {
78 : 346 : const unsigned long inode = getfdinode(tcp, fd);
79 [ + - ]: 346 : if (!inode)
80 : : return NL_FAMILY_ERROR;
81 : :
82 : 346 : const char *const details = get_sockaddr_by_inode(tcp, fd, inode);
83 [ + - ]: 346 : if (!details)
84 : : return NL_FAMILY_ERROR;
85 : :
86 : 346 : const char *const nl_details = STR_STRIP_PREFIX(details, "NETLINK:[");
87 [ + - ]: 346 : if (nl_details == details)
88 : : return NL_FAMILY_ERROR;
89 : :
90 : : const struct xlat *xlats = netlink_protocols;
91 [ + - ]: 1836 : for (; xlats->str; ++xlats) {
92 : 3672 : const char *name = STR_STRIP_PREFIX(xlats->str, "NETLINK_");
93 [ + + ]: 1836 : if (!strncmp(nl_details, name, strlen(name)))
94 : 346 : return xlats->val;
95 : : }
96 : :
97 [ # # ]: 0 : if (*nl_details >= '0' && *nl_details <= '9')
98 : 0 : return atoi(nl_details);
99 : :
100 : : return NL_FAMILY_ERROR;
101 : : }
102 : :
103 : : static void
104 : 446 : decode_nlmsg_type_default(const struct xlat *const xlat,
105 : : const uint16_t type,
106 : : const char *const dflt)
107 : : {
108 : 446 : printxval(xlat, type, dflt);
109 : 446 : }
110 : :
111 : : static void
112 : 2 : decode_nlmsg_type_generic(const struct xlat *const xlat,
113 : : const uint16_t type,
114 : : const char *const dflt)
115 : : {
116 : 2 : printxval(genl_families_xlat(), type, dflt);
117 : 2 : }
118 : :
119 : : static void
120 : 6 : decode_nlmsg_type_netfilter(const struct xlat *const xlat,
121 : : const uint16_t type,
122 : : const char *const dflt)
123 : : {
124 : : /* Reserved control nfnetlink messages first. */
125 : 6 : const char *const text = xlookup(nl_netfilter_msg_types, type);
126 [ + + ]: 6 : if (text) {
127 : 2 : tprints(text);
128 : 2 : return;
129 : : }
130 : :
131 : : /*
132 : : * Other netfilter message types are split
133 : : * in two pieces: 8 bits subsystem and 8 bits type.
134 : : */
135 : 4 : const uint8_t subsys_id = (uint8_t) (type >> 8);
136 : 4 : const uint8_t msg_type = (uint8_t) type;
137 : :
138 : 4 : printxval(xlat, subsys_id, dflt);
139 : :
140 : : /*
141 : : * The type is subsystem specific,
142 : : * print it in numeric format for now.
143 : : */
144 : 4 : tprintf("<<8|%#x", msg_type);
145 : : }
146 : :
147 : : typedef void (*nlmsg_types_decoder_t)(const struct xlat *,
148 : : uint16_t type,
149 : : const char *dflt);
150 : :
151 : : static const struct {
152 : : const nlmsg_types_decoder_t decoder;
153 : : const struct xlat *const xlat;
154 : : const char *const dflt;
155 : : } nlmsg_types[] = {
156 : : [NETLINK_AUDIT] = { NULL, nl_audit_types, "AUDIT_???" },
157 : : [NETLINK_CRYPTO] = { NULL, nl_crypto_types, "CRYPTO_MSG_???" },
158 : : [NETLINK_GENERIC] = {
159 : : decode_nlmsg_type_generic,
160 : : NULL,
161 : : "GENERIC_FAMILY_???"
162 : : },
163 : : [NETLINK_NETFILTER] = {
164 : : decode_nlmsg_type_netfilter,
165 : : nl_netfilter_subsys_ids,
166 : : "NFNL_SUBSYS_???"
167 : : },
168 : : [NETLINK_ROUTE] = { NULL, nl_route_types, "RTM_???" },
169 : : [NETLINK_SELINUX] = { NULL, nl_selinux_types, "SELNL_MSG_???" },
170 : : [NETLINK_SOCK_DIAG] = { NULL, nl_sock_diag_types, "SOCK_DIAG_???" },
171 : : [NETLINK_XFRM] = { NULL, nl_xfrm_types, "XFRM_MSG_???" }
172 : : };
173 : :
174 : : /*
175 : : * As all valid netlink families are positive integers, use unsigned int
176 : : * for family here to filter out NL_FAMILY_ERROR and NL_FAMILY_DEFAULT.
177 : : */
178 : : static void
179 : 454 : decode_nlmsg_type(const uint16_t type, const unsigned int family)
180 : : {
181 : 454 : nlmsg_types_decoder_t decoder = decode_nlmsg_type_default;
182 : 454 : const struct xlat *xlat = netlink_types;
183 : 454 : const char *dflt = "NLMSG_???";
184 : :
185 [ + + ]: 454 : if (type != NLMSG_DONE && family < ARRAY_SIZE(nlmsg_types)) {
186 [ + + ]: 340 : if (nlmsg_types[family].decoder)
187 : 8 : decoder = nlmsg_types[family].decoder;
188 [ + + ]: 340 : if (nlmsg_types[family].xlat)
189 : 338 : xlat = nlmsg_types[family].xlat;
190 [ + - ]: 340 : if (nlmsg_types[family].dflt)
191 : 340 : dflt = nlmsg_types[family].dflt;
192 : : }
193 : :
194 : 454 : decoder(xlat, type, dflt);
195 : 454 : }
196 : :
197 : : static void
198 : 454 : decode_nlmsg_flags(const uint16_t flags, const uint16_t type, const int family)
199 : : {
200 : 454 : const struct xlat *table = NULL;
201 : :
202 [ + + ]: 454 : if (type < NLMSG_MIN_TYPE) {
203 [ + + ]: 114 : if (type == NLMSG_ERROR)
204 : 18 : table = netlink_ack_flags;
205 : : goto end;
206 : : }
207 : :
208 [ - + + + : 340 : switch (family) {
+ ]
209 : : case NETLINK_CRYPTO:
210 [ # # # ]: 0 : switch (type) {
211 : : case CRYPTO_MSG_NEWALG:
212 : 0 : table = netlink_new_flags;
213 : 0 : break;
214 : : case CRYPTO_MSG_GETALG:
215 : 0 : table = netlink_get_flags;
216 : 0 : break;
217 : : }
218 : : break;
219 : : case NETLINK_SOCK_DIAG:
220 : 296 : table = netlink_get_flags;
221 : 296 : break;
222 : : case NETLINK_ROUTE:
223 [ + + ]: 10 : if (type == RTM_DELACTION) {
224 : : table = netlink_get_flags;
225 : : break;
226 : : }
227 [ + + + ]: 8 : switch (type & 3) {
228 : : case 0:
229 : 2 : table = netlink_new_flags;
230 : 2 : break;
231 : : case 2:
232 : 4 : table = netlink_get_flags;
233 [ + - ][ + + ]: 12 : break;
[ + + ]
234 : : }
235 : : break;
236 : : case NETLINK_XFRM:
237 : : switch (type) {
238 : : case XFRM_MSG_NEWSA:
239 : : case XFRM_MSG_NEWPOLICY:
240 : : case XFRM_MSG_NEWAE:
241 : : case XFRM_MSG_NEWSADINFO:
242 : : case XFRM_MSG_NEWSPDINFO:
243 : 4 : table = netlink_new_flags;
244 : 4 : break;
245 : :
246 : : case XFRM_MSG_GETSA:
247 : : case XFRM_MSG_GETPOLICY:
248 : : case XFRM_MSG_GETAE:
249 : : case XFRM_MSG_GETSADINFO:
250 : : case XFRM_MSG_GETSPDINFO:
251 : 2 : table = netlink_get_flags;
252 : 2 : break;
253 : : }
254 : : break;
255 : : }
256 : :
257 : : end:
258 : 454 : printflags_ex(flags, "NLM_F_???", netlink_flags, table, NULL);
259 : 454 : }
260 : :
261 : : static int
262 : 454 : print_nlmsghdr(struct tcb *tcp,
263 : : const int fd,
264 : : int family,
265 : : const struct nlmsghdr *const nlmsghdr)
266 : : {
267 : : /* print the whole structure regardless of its nlmsg_len */
268 : :
269 : 454 : tprintf("{len=%u, type=", nlmsghdr->nlmsg_len);
270 : :
271 : 908 : const int hdr_family = (nlmsghdr->nlmsg_type < NLMSG_MIN_TYPE
272 : 454 : && nlmsghdr->nlmsg_type != NLMSG_DONE)
273 : : ? NL_FAMILY_DEFAULT
274 [ + + ]: 454 : : (family != NL_FAMILY_DEFAULT
275 [ + - ]: 346 : ? family : get_fd_nl_family(tcp, fd));
276 : :
277 : 454 : decode_nlmsg_type(nlmsghdr->nlmsg_type, hdr_family);
278 : :
279 : 454 : tprints(", flags=");
280 : 454 : decode_nlmsg_flags(nlmsghdr->nlmsg_flags,
281 : 454 : nlmsghdr->nlmsg_type, hdr_family);
282 : :
283 : 454 : tprintf(", seq=%u, pid=%u}", nlmsghdr->nlmsg_seq,
284 : : nlmsghdr->nlmsg_pid);
285 : :
286 [ + - ]: 454 : return family != NL_FAMILY_DEFAULT ? family : hdr_family;
287 : : }
288 : :
289 : : static bool
290 : 4 : print_cookie(struct tcb *const tcp,
291 : : void *const elem_buf,
292 : : const size_t elem_size,
293 : : void *const opaque_data)
294 : : {
295 : 4 : tprintf("%" PRIu8, *(uint8_t *) elem_buf);
296 : :
297 : 4 : return true;
298 : : }
299 : :
300 : : static bool
301 : 2 : decode_nlmsgerr_attr_cookie(struct tcb *const tcp,
302 : : const kernel_ulong_t addr,
303 : : const unsigned int len,
304 : : const void *const opaque_data)
305 : : {
306 : : uint8_t cookie;
307 : 2 : const size_t nmemb = len / sizeof(cookie);
308 : :
309 : 2 : print_array(tcp, addr, nmemb, &cookie, sizeof(cookie),
310 : : umoven_or_printaddr, print_cookie, 0);
311 : :
312 : 2 : return true;
313 : : }
314 : :
315 : : static const nla_decoder_t nlmsgerr_nla_decoders[] = {
316 : : [NLMSGERR_ATTR_MSG] = decode_nla_str,
317 : : [NLMSGERR_ATTR_OFFS] = decode_nla_u32,
318 : : [NLMSGERR_ATTR_COOKIE] = decode_nlmsgerr_attr_cookie
319 : : };
320 : :
321 : : static void
322 : : decode_nlmsghdr_with_payload(struct tcb *const tcp,
323 : : const int fd,
324 : : int family,
325 : : const struct nlmsghdr *const nlmsghdr,
326 : : const kernel_ulong_t addr,
327 : : const kernel_ulong_t len);
328 : :
329 : : static void
330 : 16 : decode_nlmsgerr(struct tcb *const tcp,
331 : : const int fd,
332 : : const int family,
333 : : kernel_ulong_t addr,
334 : : unsigned int len,
335 : : const bool capped)
336 : : {
337 : : struct nlmsgerr err;
338 : :
339 [ + + ]: 16 : if (len < sizeof(err.error)) {
340 : 2 : printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
341 : 4 : return;
342 : : }
343 : :
344 [ + + ]: 14 : if (umove_or_printaddr(tcp, addr, &err.error))
345 : : return;
346 : :
347 : 12 : tprints("{error=");
348 [ + + ][ + + ]: 12 : if (err.error < 0 && (unsigned) -err.error < nerrnos) {
349 : 8 : tprintf("-%s", errnoent[-err.error]);
350 : : } else {
351 : 4 : tprintf("%d", err.error);
352 : : }
353 : :
354 : 12 : addr += offsetof(struct nlmsgerr, msg);
355 : 12 : len -= offsetof(struct nlmsgerr, msg);
356 : :
357 [ + + ]: 12 : if (len) {
358 : 8 : tprints(", msg=");
359 [ + + ]: 8 : if (fetch_nlmsghdr(tcp, &err.msg, addr, len)) {
360 [ + + ]: 6 : unsigned int payload =
361 : : capped ? sizeof(err.msg) : err.msg.nlmsg_len;
362 [ - + ]: 6 : if (payload > len)
363 : 0 : payload = len;
364 : :
365 : 6 : decode_nlmsghdr_with_payload(tcp, fd, family,
366 : : &err.msg, addr, payload);
367 [ + + ]: 6 : if (len > payload) {
368 : 2 : tprints(", ");
369 : 2 : decode_nlattr(tcp, addr + payload,
370 : : len - payload, nlmsgerr_attrs,
371 : : "NLMSGERR_ATTR_???",
372 : : nlmsgerr_nla_decoders,
373 : : ARRAY_SIZE(nlmsgerr_nla_decoders),
374 : : NULL);
375 : : }
376 : : }
377 : : }
378 : :
379 : 12 : tprints("}");
380 : : }
381 : :
382 : : static const netlink_decoder_t netlink_decoders[] = {
383 : : #ifdef HAVE_LINUX_CRYPTOUSER_H
384 : : [NETLINK_CRYPTO] = decode_netlink_crypto,
385 : : #endif
386 : : [NETLINK_SELINUX] = decode_netlink_selinux,
387 : : [NETLINK_SOCK_DIAG] = decode_netlink_sock_diag
388 : : };
389 : :
390 : : static void
391 : 346 : decode_payload(struct tcb *const tcp,
392 : : const int fd,
393 : : const int family,
394 : : const struct nlmsghdr *const nlmsghdr,
395 : : const kernel_ulong_t addr,
396 : : const unsigned int len)
397 : : {
398 [ + + ]: 346 : if (nlmsghdr->nlmsg_type == NLMSG_ERROR) {
399 : 16 : decode_nlmsgerr(tcp, fd, family, addr, len,
400 : 16 : nlmsghdr->nlmsg_flags & NLM_F_CAPPED);
401 : 16 : return;
402 : : }
403 : :
404 [ + + ]: 330 : if ((unsigned int) family < ARRAY_SIZE(netlink_decoders)
405 [ + - ]: 312 : && netlink_decoders[family]
406 [ + + ]: 312 : && netlink_decoders[family](tcp, nlmsghdr, addr, len)) {
407 : : return;
408 : : }
409 : :
410 [ + + ][ + + ]: 26 : if (nlmsghdr->nlmsg_type == NLMSG_DONE && len == sizeof(int)) {
411 : : int num;
412 : :
413 [ + + ]: 4 : if (!umove_or_printaddr(tcp, addr, &num))
414 : 4 : tprintf("%d", num);
415 : : return;
416 : : }
417 : :
418 : 22 : printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
419 : : }
420 : :
421 : : static void
422 : 454 : decode_nlmsghdr_with_payload(struct tcb *const tcp,
423 : : const int fd,
424 : : int family,
425 : : const struct nlmsghdr *const nlmsghdr,
426 : : const kernel_ulong_t addr,
427 : : const kernel_ulong_t len)
428 : : {
429 : 454 : const unsigned int nlmsg_len =
430 : 454 : nlmsghdr->nlmsg_len > len ? len : nlmsghdr->nlmsg_len;
431 : :
432 [ + + ]: 454 : if (nlmsg_len > NLMSG_HDRLEN)
433 : 346 : tprints("{");
434 : :
435 : 454 : family = print_nlmsghdr(tcp, fd, family, nlmsghdr);
436 : :
437 [ + + ]: 454 : if (nlmsg_len > NLMSG_HDRLEN) {
438 : 346 : tprints(", ");
439 : 346 : decode_payload(tcp, fd, family, nlmsghdr, addr + NLMSG_HDRLEN,
440 : : nlmsg_len - NLMSG_HDRLEN);
441 : 346 : tprints("}");
442 : : }
443 : 454 : }
444 : :
445 : : void
446 : 392 : decode_netlink(struct tcb *const tcp,
447 : : const int fd,
448 : : kernel_ulong_t addr,
449 : : kernel_ulong_t len)
450 : : {
451 : : struct nlmsghdr nlmsghdr;
452 : 392 : bool print_array = false;
453 : : unsigned int elt;
454 : :
455 [ + + ]: 464 : for (elt = 0; fetch_nlmsghdr(tcp, &nlmsghdr, addr, len); elt++) {
456 [ + + ][ + + ]: 450 : if (abbrev(tcp) && elt == max_strlen) {
457 : 2 : tprints("...");
458 : 2 : break;
459 : : }
460 : :
461 : 448 : unsigned int nlmsg_len = NLMSG_ALIGN(nlmsghdr.nlmsg_len);
462 : 448 : kernel_ulong_t next_addr = 0;
463 : 448 : kernel_ulong_t next_len = 0;
464 : :
465 [ + + ]: 448 : if (nlmsghdr.nlmsg_len >= NLMSG_HDRLEN) {
466 [ + + ]: 444 : next_len = (len >= nlmsg_len) ? len - nlmsg_len : 0;
467 : :
468 [ + + ][ + - ]: 444 : if (next_len && addr + nlmsg_len > addr)
469 : 72 : next_addr = addr + nlmsg_len;
470 : : }
471 : :
472 [ + + ]: 448 : if (!print_array && next_addr) {
473 : 10 : tprints("[");
474 : 10 : print_array = true;
475 : : }
476 : :
477 : 448 : decode_nlmsghdr_with_payload(tcp, fd, NL_FAMILY_DEFAULT,
478 : : &nlmsghdr, addr, len);
479 : :
480 [ + + ]: 448 : if (!next_addr)
481 : : break;
482 : :
483 : 72 : tprints(", ");
484 : 72 : addr = next_addr;
485 : 72 : len = next_len;
486 : : }
487 : :
488 [ + + ]: 392 : if (print_array) {
489 : 10 : tprints("]");
490 : : }
491 : 392 : }
|