1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import socket
7
8from autotest_lib.client.bin import test, utils
9from autotest_lib.client.common_lib import error
10
11
12# From <bits/socket.h> on Linux 3.2, EGLIBC 2.15.
13PROTOCOL_FAMILIES_STR = """
14#define PF_UNSPEC       0       /* Unspecified.  */
15#define PF_LOCAL        1       /* Local to host (pipes and file-domain).  */
16#define PF_UNIX         1       /* POSIX name for PF_LOCAL.  */
17#define PF_FILE         1       /* Another non-standard name for PF_LOCAL.  */
18#define PF_INET         2       /* IP protocol family.  */
19#define PF_AX25         3       /* Amateur Radio AX.25.  */
20#define PF_IPX          4       /* Novell Internet Protocol.  */
21#define PF_APPLETALK    5       /* Appletalk DDP.  */
22#define PF_NETROM       6       /* Amateur radio NetROM.  */
23#define PF_BRIDGE       7       /* Multiprotocol bridge.  */
24#define PF_ATMPVC       8       /* ATM PVCs.  */
25#define PF_X25          9       /* Reserved for X.25 project.  */
26#define PF_INET6        10      /* IP version 6.  */
27#define PF_ROSE         11      /* Amateur Radio X.25 PLP.  */
28#define PF_DECnet       12      /* Reserved for DECnet project.  */
29#define PF_NETBEUI      13      /* Reserved for 802.2LLC project.  */
30#define PF_SECURITY     14      /* Security callback pseudo AF.  */
31#define PF_KEY          15      /* PF_KEY key management API.  */
32#define PF_NETLINK      16
33#define PF_ROUTE        16      /* Alias to emulate 4.4BSD.  */
34#define PF_PACKET       17      /* Packet family.  */
35#define PF_ASH          18      /* Ash.  */
36#define PF_ECONET       19      /* Acorn Econet.  */
37#define PF_ATMSVC       20      /* ATM SVCs.  */
38#define PF_RDS          21      /* RDS sockets.  */
39#define PF_SNA          22      /* Linux SNA Project */
40#define PF_IRDA         23      /* IRDA sockets.  */
41#define PF_PPPOX        24      /* PPPoX sockets.  */
42#define PF_WANPIPE      25      /* Wanpipe API sockets.  */
43#define PF_LLC          26      /* Linux LLC.  */
44#define PF_CAN          29      /* Controller Area Network.  */
45#define PF_TIPC         30      /* TIPC sockets.  */
46#define PF_BLUETOOTH    31      /* Bluetooth sockets.  */
47#define PF_IUCV         32      /* IUCV sockets.  */
48#define PF_RXRPC        33      /* RxRPC sockets.  */
49#define PF_ISDN         34      /* mISDN sockets.  */
50#define PF_PHONET       35      /* Phonet sockets.  */
51#define PF_IEEE802154   36      /* IEEE 802.15.4 sockets.  */
52#define PF_CAIF         37      /* CAIF sockets.  */
53#define PF_ALG          38      /* Algorithm sockets.  */
54#define PF_NFC          39      /* NFC sockets.  */
55#define PF_MAX          40      /* For now...  */
56"""
57
58PROTOCOL_FAMILIES = dict([(int(line.split()[2]), line.split()[1])
59                          for line
60                          in PROTOCOL_FAMILIES_STR.strip().splitlines()])
61
62SOCKET_TYPES = [socket.SOCK_STREAM, socket.SOCK_DGRAM, socket.SOCK_RAW,
63                socket.SOCK_RDM, socket.SOCK_SEQPACKET]
64
65
66class security_ProtocolFamilies(test.test):
67    version = 1
68    PF_BASELINE = ["PF_FILE", "PF_PACKET", "PF_INET", "PF_INET6", "PF_ROUTE",
69                   "PF_LOCAL", "PF_NETLINK", "PF_UNIX", "PF_BLUETOOTH", "PF_ALG"]
70    PER_BOARD = {}
71
72
73    def pf_name(self, pfn):
74        protocol_family = ""
75
76        if pfn in PROTOCOL_FAMILIES:
77            protocol_family = PROTOCOL_FAMILIES[pfn]
78        else:
79            protocol_family = "PF %d (unknown)" % pfn
80
81        return protocol_family
82
83
84    def is_protocol_family_available(self, pfn):
85        """Tries to create a socket with protocol family number |pfn|
86        and every possible socket type.
87        Returns |True| if any socket can be created, |False| otherwise.
88        """
89
90        available = False
91
92        for socket_type in SOCKET_TYPES:
93            try:
94                socket.socket(pfn, socket_type)
95                available = True
96                logging.info("%s available with socket type %d" %
97                             (self.pf_name(pfn), socket_type))
98                break
99            except socket.error:
100                pass
101
102        return available
103
104
105    def run_once(self):
106        """Tries to create a socket with every possible combination of
107        protocol family and socket type.
108        Fails if it can create a socket for one or more protocol families
109        not in the baseline.
110        """
111
112        unexpected_protocol_families = []
113
114        # Protocol families currently go up to 40, but this way we make sure
115        # to catch new families that might get added to the kernel.
116        for pfn in range(256):
117            pf_available = self.is_protocol_family_available(pfn)
118            protocol_family = self.pf_name(pfn)
119
120            if pf_available:
121                # If PF is in baseline, continue.
122                if protocol_family in self.PF_BASELINE:
123                    continue
124
125                # Check the board-specific whitelist.
126                current_board = utils.get_current_board()
127                board_pfs = self.PER_BOARD.get(current_board, None)
128                if not board_pfs or protocol_family not in board_pfs:
129                    unexpected_protocol_families.append(protocol_family)
130
131        if len(unexpected_protocol_families) > 0:
132            failure_string = "Unexpected protocol families available: "
133            failure_string += ", ".join(unexpected_protocol_families)
134            logging.error(failure_string)
135            raise error.TestFail(failure_string)
136