Coverage Report

Created: 2025-12-28 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sudo/plugins/sudoers/match_addr.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 1996, 1998-2005, 2007-2015
5
 *  Todd C. Miller <Todd.Miller@sudo.ws>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 *
19
 * Sponsored in part by the Defense Advanced Research Projects
20
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22
 */
23
24
#include <config.h>
25
26
#include <sys/types.h>
27
#include <sys/socket.h>
28
#include <stdio.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <unistd.h>
32
#include <netinet/in.h>
33
#include <arpa/inet.h>
34
#ifdef NEED_RESOLV_H
35
# include <arpa/nameser.h>
36
# include <resolv.h>
37
#endif /* NEED_RESOLV_H */
38
39
#include <sudoers.h>
40
#include <interfaces.h>
41
42
static int
43
addr_matches_if(const char *n)
44
0
{
45
0
    union sudo_in_addr_un addr;
46
0
    struct interface *ifp;
47
0
#ifdef HAVE_STRUCT_IN6_ADDR
48
0
    size_t j;
49
0
#endif
50
0
    unsigned int family;
51
0
    debug_decl(addr_matches_if, SUDOERS_DEBUG_MATCH);
52
53
0
#ifdef HAVE_STRUCT_IN6_ADDR
54
0
    if (inet_pton(AF_INET6, n, &addr.ip6) == 1) {
55
0
  family = AF_INET6;
56
0
    } else
57
0
#endif /* HAVE_STRUCT_IN6_ADDR */
58
0
    if (inet_pton(AF_INET, n, &addr.ip4) == 1) {
59
0
  family = AF_INET;
60
0
    } else {
61
0
  debug_return_int(DENY);
62
0
    }
63
64
0
    SLIST_FOREACH(ifp, get_interfaces(), entries) {
65
0
  if (ifp->family != family)
66
0
      continue;
67
0
  switch (family) {
68
0
      case AF_INET:
69
0
    if (ifp->addr.ip4.s_addr == addr.ip4.s_addr ||
70
0
        (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
71
0
        == addr.ip4.s_addr)
72
0
        debug_return_int(ALLOW);
73
0
    break;
74
0
#ifdef HAVE_STRUCT_IN6_ADDR
75
0
      case AF_INET6:
76
0
    if (memcmp(ifp->addr.ip6.s6_addr, addr.ip6.s6_addr,
77
0
        sizeof(addr.ip6.s6_addr)) == 0)
78
0
        debug_return_int(ALLOW);
79
0
    for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) {
80
0
        if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j])
81
0
      break;
82
0
    }
83
0
    if (j == sizeof(addr.ip6.s6_addr))
84
0
        debug_return_int(ALLOW);
85
0
    break;
86
0
#endif /* HAVE_STRUCT_IN6_ADDR */
87
0
  }
88
0
    }
89
90
0
    debug_return_int(DENY);
91
0
}
92
93
static int
94
addr_matches_if_netmask(const char *n, const char *m)
95
0
{
96
0
    size_t i;
97
0
    union sudo_in_addr_un addr, mask;
98
0
    struct interface *ifp;
99
0
#ifdef HAVE_STRUCT_IN6_ADDR
100
0
    size_t j;
101
0
#endif
102
0
    unsigned int family;
103
0
    const char *errstr;
104
0
    debug_decl(addr_matches_if, SUDOERS_DEBUG_MATCH);
105
106
0
#ifdef HAVE_STRUCT_IN6_ADDR
107
0
    if (inet_pton(AF_INET6, n, &addr.ip6) == 1)
108
0
  family = AF_INET6;
109
0
    else
110
0
#endif /* HAVE_STRUCT_IN6_ADDR */
111
0
    if (inet_pton(AF_INET, n, &addr.ip4) == 1) {
112
0
  family = AF_INET;
113
0
    } else {
114
0
  debug_return_int(DENY);
115
0
    }
116
117
0
    if (family == AF_INET) {
118
0
  if (strchr(m, '.')) {
119
0
      if (inet_pton(AF_INET, m, &mask.ip4) != 1) {
120
0
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
121
0
        "IPv4 netmask %s: %s", m, "invalid value");
122
0
    debug_return_int(DENY);
123
0
      }
124
0
  } else {
125
0
      i = (size_t)sudo_strtonum(m, 1, 32, &errstr);
126
0
      if (errstr != NULL) {
127
0
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
128
0
        "IPv4 netmask %s: %s", m, errstr);
129
0
    debug_return_int(DENY);
130
0
      }
131
0
      mask.ip4.s_addr = htonl(0xffffffffU << (32 - i));
132
0
  }
133
0
  addr.ip4.s_addr &= mask.ip4.s_addr;
134
0
    }
135
0
#ifdef HAVE_STRUCT_IN6_ADDR
136
0
    else {
137
0
  if (inet_pton(AF_INET6, m, &mask.ip6) != 1) {
138
0
      j = (size_t)sudo_strtonum(m, 1, 128, &errstr);
139
0
      if (errstr != NULL) {
140
0
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
141
0
        "IPv6 netmask %s: %s", m, errstr);
142
0
    debug_return_int(DENY);
143
0
      }
144
0
      for (i = 0; i < sizeof(addr.ip6.s6_addr); i++) {
145
0
    if (j < i * 8)
146
0
        mask.ip6.s6_addr[i] = 0;
147
0
    else if (i * 8 + 8 <= j)
148
0
        mask.ip6.s6_addr[i] = 0xff;
149
0
    else
150
0
        mask.ip6.s6_addr[i] = 0xff00 >> (j - i * 8);
151
0
    addr.ip6.s6_addr[i] &= mask.ip6.s6_addr[i];
152
0
      }
153
0
  }
154
0
    }
155
0
#endif /* HAVE_STRUCT_IN6_ADDR */
156
157
0
    SLIST_FOREACH(ifp, get_interfaces(), entries) {
158
0
  if (ifp->family != family)
159
0
      continue;
160
0
  switch (family) {
161
0
      case AF_INET:
162
0
    if ((ifp->addr.ip4.s_addr & mask.ip4.s_addr) == addr.ip4.s_addr)
163
0
        debug_return_int(ALLOW);
164
0
    break;
165
0
#ifdef HAVE_STRUCT_IN6_ADDR
166
0
      case AF_INET6:
167
0
    for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) {
168
0
        if ((ifp->addr.ip6.s6_addr[j] & mask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j])
169
0
      break;
170
0
    }
171
0
    if (j == sizeof(addr.ip6.s6_addr))
172
0
        debug_return_int(ALLOW);
173
0
    break;
174
0
#endif /* HAVE_STRUCT_IN6_ADDR */
175
0
  }
176
0
    }
177
178
0
    debug_return_int(DENY);
179
0
}
180
181
/*
182
 * Returns ALLOW if "n" is one of our ip addresses or if
183
 * "n" is a network that we are on, else returns DENY.
184
 */
185
int
186
addr_matches(char *n)
187
0
{
188
0
    char *m;
189
0
    int ret;
190
0
    debug_decl(addr_matches, SUDOERS_DEBUG_MATCH);
191
192
    /* If there's an explicit netmask, use it. */
193
0
    if ((m = strchr(n, '/'))) {
194
0
  *m++ = '\0';
195
0
  ret = addr_matches_if_netmask(n, m);
196
0
  *(m - 1) = '/';
197
0
    } else
198
0
  ret = addr_matches_if(n);
199
200
0
    sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
201
0
  "IP address %s matches local host: %s", n,
202
0
  ret == ALLOW ? "ALLOW" : "DENY");
203
0
    debug_return_int(ret);
204
0
}