Coverage Report

Created: 2025-07-23 06:42

/src/sudo/plugins/sudoers/match_addr.c
Line
Count
Source (jump to first uncovered line)
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
/*
25
 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
26
 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
27
 */
28
29
#include <config.h>
30
31
#include <sys/types.h>
32
#include <sys/socket.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <unistd.h>
37
#include <netinet/in.h>
38
#include <arpa/inet.h>
39
#ifdef NEED_RESOLV_H
40
# include <arpa/nameser.h>
41
# include <resolv.h>
42
#endif /* NEED_RESOLV_H */
43
44
#include <sudoers.h>
45
#include <interfaces.h>
46
47
static int
48
addr_matches_if(const char *n)
49
0
{
50
0
    union sudo_in_addr_un addr;
51
0
    struct interface *ifp;
52
0
#ifdef HAVE_STRUCT_IN6_ADDR
53
0
    size_t j;
54
0
#endif
55
0
    unsigned int family;
56
0
    debug_decl(addr_matches_if, SUDOERS_DEBUG_MATCH);
57
58
0
#ifdef HAVE_STRUCT_IN6_ADDR
59
0
    if (inet_pton(AF_INET6, n, &addr.ip6) == 1) {
60
0
  family = AF_INET6;
61
0
    } else
62
0
#endif /* HAVE_STRUCT_IN6_ADDR */
63
0
    if (inet_pton(AF_INET, n, &addr.ip4) == 1) {
64
0
  family = AF_INET;
65
0
    } else {
66
0
  debug_return_int(DENY);
67
0
    }
68
69
0
    SLIST_FOREACH(ifp, get_interfaces(), entries) {
70
0
  if (ifp->family != family)
71
0
      continue;
72
0
  switch (family) {
73
0
      case AF_INET:
74
0
    if (ifp->addr.ip4.s_addr == addr.ip4.s_addr ||
75
0
        (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
76
0
        == addr.ip4.s_addr)
77
0
        debug_return_int(ALLOW);
78
0
    break;
79
0
#ifdef HAVE_STRUCT_IN6_ADDR
80
0
      case AF_INET6:
81
0
    if (memcmp(ifp->addr.ip6.s6_addr, addr.ip6.s6_addr,
82
0
        sizeof(addr.ip6.s6_addr)) == 0)
83
0
        debug_return_int(ALLOW);
84
0
    for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) {
85
0
        if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j])
86
0
      break;
87
0
    }
88
0
    if (j == sizeof(addr.ip6.s6_addr))
89
0
        debug_return_int(ALLOW);
90
0
    break;
91
0
#endif /* HAVE_STRUCT_IN6_ADDR */
92
0
  }
93
0
    }
94
95
0
    debug_return_int(DENY);
96
0
}
97
98
static int
99
addr_matches_if_netmask(const char *n, const char *m)
100
0
{
101
0
    size_t i;
102
0
    union sudo_in_addr_un addr, mask;
103
0
    struct interface *ifp;
104
0
#ifdef HAVE_STRUCT_IN6_ADDR
105
0
    size_t j;
106
0
#endif
107
0
    unsigned int family;
108
0
    const char *errstr;
109
0
    debug_decl(addr_matches_if, SUDOERS_DEBUG_MATCH);
110
111
0
#ifdef HAVE_STRUCT_IN6_ADDR
112
0
    if (inet_pton(AF_INET6, n, &addr.ip6) == 1)
113
0
  family = AF_INET6;
114
0
    else
115
0
#endif /* HAVE_STRUCT_IN6_ADDR */
116
0
    if (inet_pton(AF_INET, n, &addr.ip4) == 1) {
117
0
  family = AF_INET;
118
0
    } else {
119
0
  debug_return_int(DENY);
120
0
    }
121
122
0
    if (family == AF_INET) {
123
0
  if (strchr(m, '.')) {
124
0
      if (inet_pton(AF_INET, m, &mask.ip4) != 1) {
125
0
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
126
0
        "IPv4 netmask %s: %s", m, "invalid value");
127
0
    debug_return_int(DENY);
128
0
      }
129
0
  } else {
130
0
      i = (size_t)sudo_strtonum(m, 1, 32, &errstr);
131
0
      if (errstr != NULL) {
132
0
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
133
0
        "IPv4 netmask %s: %s", m, errstr);
134
0
    debug_return_int(DENY);
135
0
      }
136
0
      mask.ip4.s_addr = htonl(0xffffffffU << (32 - i));
137
0
  }
138
0
  addr.ip4.s_addr &= mask.ip4.s_addr;
139
0
    }
140
0
#ifdef HAVE_STRUCT_IN6_ADDR
141
0
    else {
142
0
  if (inet_pton(AF_INET6, m, &mask.ip6) != 1) {
143
0
      j = (size_t)sudo_strtonum(m, 1, 128, &errstr);
144
0
      if (errstr != NULL) {
145
0
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
146
0
        "IPv6 netmask %s: %s", m, errstr);
147
0
    debug_return_int(DENY);
148
0
      }
149
0
      for (i = 0; i < sizeof(addr.ip6.s6_addr); i++) {
150
0
    if (j < i * 8)
151
0
        mask.ip6.s6_addr[i] = 0;
152
0
    else if (i * 8 + 8 <= j)
153
0
        mask.ip6.s6_addr[i] = 0xff;
154
0
    else
155
0
        mask.ip6.s6_addr[i] = 0xff00 >> (j - i * 8);
156
0
    addr.ip6.s6_addr[i] &= mask.ip6.s6_addr[i];
157
0
      }
158
0
  }
159
0
    }
160
0
#endif /* HAVE_STRUCT_IN6_ADDR */
161
162
0
    SLIST_FOREACH(ifp, get_interfaces(), entries) {
163
0
  if (ifp->family != family)
164
0
      continue;
165
0
  switch (family) {
166
0
      case AF_INET:
167
0
    if ((ifp->addr.ip4.s_addr & mask.ip4.s_addr) == addr.ip4.s_addr)
168
0
        debug_return_int(ALLOW);
169
0
    break;
170
0
#ifdef HAVE_STRUCT_IN6_ADDR
171
0
      case AF_INET6:
172
0
    for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) {
173
0
        if ((ifp->addr.ip6.s6_addr[j] & mask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j])
174
0
      break;
175
0
    }
176
0
    if (j == sizeof(addr.ip6.s6_addr))
177
0
        debug_return_int(ALLOW);
178
0
    break;
179
0
#endif /* HAVE_STRUCT_IN6_ADDR */
180
0
  }
181
0
    }
182
183
0
    debug_return_int(DENY);
184
0
}
185
186
/*
187
 * Returns ALLOW if "n" is one of our ip addresses or if
188
 * "n" is a network that we are on, else returns DENY.
189
 */
190
int
191
addr_matches(char *n)
192
0
{
193
0
    char *m;
194
0
    int ret;
195
0
    debug_decl(addr_matches, SUDOERS_DEBUG_MATCH);
196
197
    /* If there's an explicit netmask, use it. */
198
0
    if ((m = strchr(n, '/'))) {
199
0
  *m++ = '\0';
200
0
  ret = addr_matches_if_netmask(n, m);
201
0
  *(m - 1) = '/';
202
0
    } else
203
0
  ret = addr_matches_if(n);
204
205
0
    sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
206
0
  "IP address %s matches local host: %s", n,
207
0
  ret == ALLOW ? "ALLOW" : "DENY");
208
0
    debug_return_int(ret);
209
0
}