Coverage Report

Created: 2025-07-01 07:00

/src/openssh/openbsd-compat/port-net.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2005 Reyk Floeter <reyk@openbsd.org>
3
 *
4
 * Permission to use, copy, modify, and distribute this software for any
5
 * purpose with or without fee is hereby granted, provided that the above
6
 * copyright notice and this permission notice appear in all copies.
7
 *
8
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
 */
16
17
#include "includes.h"
18
19
#include <sys/types.h>
20
#include <sys/ioctl.h>
21
22
#include <netinet/in.h>
23
#include <arpa/inet.h>
24
#include <netinet/ip.h>
25
26
#include <errno.h>
27
#include <fcntl.h>
28
#include <stdarg.h>
29
#include <stdio.h>
30
#include <string.h>
31
#include <unistd.h>
32
33
#include "openbsd-compat/sys-queue.h"
34
#include "log.h"
35
#include "misc.h"
36
#include "sshbuf.h"
37
#include "channels.h"
38
#include "ssherr.h"
39
40
/*
41
 * This file contains various portability code for network support,
42
 * including tun/tap forwarding and routing domains.
43
 */
44
45
#if defined(SYS_RDOMAIN_LINUX) || defined(SSH_TUN_LINUX)
46
#include <linux/if.h>
47
#endif
48
49
#if defined(SYS_RDOMAIN_LINUX)
50
char *
51
sys_get_rdomain(int fd)
52
0
{
53
0
  char dev[IFNAMSIZ + 1];
54
0
  socklen_t len = sizeof(dev) - 1;
55
56
0
  if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, dev, &len) == -1) {
57
0
    error("%s: cannot determine VRF for fd=%d : %s",
58
0
        __func__, fd, strerror(errno));
59
0
    return NULL;
60
0
  }
61
0
  dev[len] = '\0';
62
0
  return strdup(dev);
63
0
}
64
65
int
66
sys_set_rdomain(int fd, const char *name)
67
0
{
68
0
  if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
69
0
      name, strlen(name)) == -1) {
70
0
    error("%s: setsockopt(%d, SO_BINDTODEVICE, %s): %s",
71
0
        __func__, fd, name, strerror(errno));
72
0
    return -1;
73
0
  }
74
0
  return 0;
75
0
}
76
77
int
78
sys_valid_rdomain(const char *name)
79
0
{
80
0
  int fd;
81
82
  /*
83
   * This is a pretty crappy way to test. It would be better to
84
   * check whether "name" represents a VRF device, but apparently
85
   * that requires an rtnetlink transaction.
86
   */
87
0
  if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
88
0
    return 0;
89
0
  if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
90
0
      name, strlen(name)) == -1) {
91
0
    close(fd);
92
0
    return 0;
93
0
  }
94
0
  close(fd);
95
0
  return 1;
96
0
}
97
#elif defined(SYS_RDOMAIN_XXX)
98
/* XXX examples */
99
char *
100
sys_get_rdomain(int fd)
101
{
102
  return NULL;
103
}
104
105
int
106
sys_set_rdomain(int fd, const char *name)
107
{
108
  return -1;
109
}
110
111
int
112
valid_rdomain(const char *name)
113
{
114
  return 0;
115
}
116
117
void
118
sys_set_process_rdomain(const char *name)
119
{
120
  fatal("%s: not supported", __func__);
121
}
122
#endif /* defined(SYS_RDOMAIN_XXX) */
123
124
/*
125
 * This is the portable version of the SSH tunnel forwarding, it
126
 * uses some preprocessor definitions for various platform-specific
127
 * settings.
128
 *
129
 * SSH_TUN_LINUX  Use the (newer) Linux tun/tap device
130
 * SSH_TUN_FREEBSD  Use the FreeBSD tun/tap device
131
 * SSH_TUN_COMPAT_AF  Translate the OpenBSD address family
132
 * SSH_TUN_PREPEND_AF Prepend/remove the address family
133
 */
134
135
/*
136
 * System-specific tunnel open function
137
 */
138
139
#if defined(SSH_TUN_LINUX)
140
#include <linux/if_tun.h>
141
0
#define TUN_CTRL_DEV "/dev/net/tun"
142
143
int
144
sys_tun_open(int tun, int mode, char **ifname)
145
0
{
146
0
  struct ifreq ifr;
147
0
  int fd = -1;
148
0
  const char *name = NULL;
149
150
0
  if (ifname != NULL)
151
0
    *ifname = NULL;
152
0
  if ((fd = open(TUN_CTRL_DEV, O_RDWR)) == -1) {
153
0
    debug("%s: failed to open tunnel control device \"%s\": %s",
154
0
        __func__, TUN_CTRL_DEV, strerror(errno));
155
0
    return (-1);
156
0
  }
157
158
0
  bzero(&ifr, sizeof(ifr));
159
160
0
  if (mode == SSH_TUNMODE_ETHERNET) {
161
0
    ifr.ifr_flags = IFF_TAP;
162
0
    name = "tap%d";
163
0
  } else {
164
0
    ifr.ifr_flags = IFF_TUN;
165
0
    name = "tun%d";
166
0
  }
167
0
  ifr.ifr_flags |= IFF_NO_PI;
168
169
0
  if (tun != SSH_TUNID_ANY) {
170
0
    if (tun > SSH_TUNID_MAX) {
171
0
      debug("%s: invalid tunnel id %x: %s", __func__,
172
0
          tun, strerror(errno));
173
0
      goto failed;
174
0
    }
175
0
    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), name, tun);
176
0
  }
177
178
0
  if (ioctl(fd, TUNSETIFF, &ifr) == -1) {
179
0
    debug("%s: failed to configure tunnel (mode %d): %s", __func__,
180
0
        mode, strerror(errno));
181
0
    goto failed;
182
0
  }
183
184
0
  if (tun == SSH_TUNID_ANY)
185
0
    debug("%s: tunnel mode %d fd %d", __func__, mode, fd);
186
0
  else
187
0
    debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd);
188
189
0
  if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL)
190
0
    goto failed;
191
192
0
  return (fd);
193
194
0
 failed:
195
0
  close(fd);
196
0
  return (-1);
197
0
}
198
#endif /* SSH_TUN_LINUX */
199
200
#ifdef SSH_TUN_FREEBSD
201
#include <sys/socket.h>
202
#include <net/if.h>
203
204
#ifdef HAVE_NET_IF_TUN_H
205
#include <net/if_tun.h>
206
#endif
207
208
int
209
sys_tun_open(int tun, int mode, char **ifname)
210
{
211
  struct ifreq ifr;
212
  char name[100];
213
  int fd = -1, sock;
214
  const char *tunbase = "tun";
215
#if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF)
216
  int flag;
217
#endif
218
219
  if (ifname != NULL)
220
    *ifname = NULL;
221
222
  if (mode == SSH_TUNMODE_ETHERNET) {
223
#ifdef SSH_TUN_NO_L2
224
    debug("%s: no layer 2 tunnelling support", __func__);
225
    return (-1);
226
#else
227
    tunbase = "tap";
228
#endif
229
  }
230
231
  /* Open the tunnel device */
232
  if (tun <= SSH_TUNID_MAX) {
233
    snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun);
234
    fd = open(name, O_RDWR);
235
  } else if (tun == SSH_TUNID_ANY) {
236
    for (tun = 100; tun >= 0; tun--) {
237
      snprintf(name, sizeof(name), "/dev/%s%d",
238
          tunbase, tun);
239
      if ((fd = open(name, O_RDWR)) >= 0)
240
        break;
241
    }
242
  } else {
243
    debug("%s: invalid tunnel %u\n", __func__, tun);
244
    return (-1);
245
  }
246
247
  if (fd < 0) {
248
    debug("%s: %s open failed: %s", __func__, name,
249
        strerror(errno));
250
    return (-1);
251
  }
252
253
  /* Turn on tunnel headers */
254
#if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF)
255
  flag = 1;
256
  if (mode != SSH_TUNMODE_ETHERNET &&
257
      ioctl(fd, TUNSIFHEAD, &flag) == -1) {
258
    debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd,
259
        strerror(errno));
260
    close(fd);
261
  }
262
#endif
263
264
  debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
265
266
  /* Set the tunnel device operation mode */
267
  snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun);
268
  if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
269
    goto failed;
270
271
  if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
272
    goto failed;
273
  if ((ifr.ifr_flags & IFF_UP) == 0) {
274
    ifr.ifr_flags |= IFF_UP;
275
    if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
276
      goto failed;
277
  }
278
279
  if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL)
280
    goto failed;
281
282
  close(sock);
283
  return (fd);
284
285
 failed:
286
  if (fd >= 0)
287
    close(fd);
288
  if (sock >= 0)
289
    close(sock);
290
  debug("%s: failed to set %s mode %d: %s", __func__, name,
291
      mode, strerror(errno));
292
  return (-1);
293
}
294
#endif /* SSH_TUN_FREEBSD */
295
296
/*
297
 * System-specific channel filters
298
 */
299
300
#if defined(SSH_TUN_FILTER)
301
/*
302
 * The tunnel forwarding protocol prepends the address family of forwarded
303
 * IP packets using OpenBSD's numbers.
304
 */
305
0
#define OPENBSD_AF_INET   2
306
0
#define OPENBSD_AF_INET6  24
307
308
int
309
sys_tun_infilter(struct ssh *ssh, struct Channel *c, char *buf, int _len)
310
0
{
311
0
  int r;
312
0
  size_t len;
313
0
  char *ptr = buf;
314
0
#if defined(SSH_TUN_PREPEND_AF)
315
0
  char rbuf[CHAN_RBUF];
316
0
  struct ip iph;
317
0
#endif
318
0
#if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF)
319
0
  u_int32_t af;
320
0
#endif
321
322
  /* XXX update channel input filter API to use unsigned length */
323
0
  if (_len < 0)
324
0
    return -1;
325
0
  len = _len;
326
327
0
#if defined(SSH_TUN_PREPEND_AF)
328
0
  if (len <= sizeof(iph) || len > sizeof(rbuf) - 4)
329
0
    return -1;
330
  /* Determine address family from packet IP header. */
331
0
  memcpy(&iph, buf, sizeof(iph));
332
0
  af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET;
333
  /* Prepend address family to packet using OpenBSD constants */
334
0
  memcpy(rbuf + 4, buf, len);
335
0
  len += 4;
336
0
  POKE_U32(rbuf, af);
337
0
  ptr = rbuf;
338
#elif defined(SSH_TUN_COMPAT_AF)
339
  /* Convert existing address family header to OpenBSD value */
340
  if (len <= 4)
341
    return -1;
342
  af = PEEK_U32(buf);
343
  /* Put it back */
344
  POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET);
345
#endif
346
347
0
  if ((r = sshbuf_put_string(c->input, ptr, len)) != 0)
348
0
    fatal("%s: buffer error: %s", __func__, ssh_err(r));
349
0
  return (0);
350
0
}
351
352
u_char *
353
sys_tun_outfilter(struct ssh *ssh, struct Channel *c,
354
    u_char **data, size_t *dlen)
355
0
{
356
0
  u_char *buf;
357
0
  u_int32_t af;
358
0
  int r;
359
360
  /* XXX new API is incompatible with this signature. */
361
0
  if ((r = sshbuf_get_string(c->output, data, dlen)) != 0)
362
0
    fatal("%s: buffer error: %s", __func__, ssh_err(r));
363
0
  if (*dlen < sizeof(af))
364
0
    return (NULL);
365
0
  buf = *data;
366
367
0
#if defined(SSH_TUN_PREPEND_AF)
368
  /* skip address family */
369
0
  *dlen -= sizeof(af);
370
0
  buf = *data + sizeof(af);
371
#elif defined(SSH_TUN_COMPAT_AF)
372
  /* translate address family */
373
  af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET;
374
  POKE_U32(buf, af);
375
#endif
376
0
  return (buf);
377
0
}
378
#endif /* SSH_TUN_FILTER */