aboutsummaryrefslogtreecommitdiffstats
path: root/sysdep/bsd/setkey.h
blob: 40564cf1337a23208b0e2d3e1f4b93aa5c8e347d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*
 *	BIRD -- Manipulation the IPsec SA/SP database using setkey(8) utility
 *
 * 	(c) 2016 CZ.NIC z.s.p.o.
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/pfkeyv2.h>
#include <netipsec/ipsec.h>

#include "nest/bird.h"
#include "sysdep/unix/unix.h"


/*
 * Open a socket for manage the IPsec SA/SP database entries
 */
static int
setkey_open_socket(void)
{
  int s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
  if (s < 0)
  {
    log(L_ERR "SETKEY: socket: %m");
    return -1;
  }

  return s;
}

static int
setkey_send(struct sadb_msg *msg, uint len)
{
  int s = setkey_open_socket();
  if (s < 0)
    return -1;

  if (msg->sadb_msg_type == SADB_ADD)
  {
    /* Delete possible current key in the IPsec SA/SP database */
    msg->sadb_msg_type = SADB_DELETE;
    send(s, msg, len, 0);
    msg->sadb_msg_type = SADB_ADD;
  }

  if (send(s, msg, len, 0) < 0)
  {
    log(L_ERR "SETKEY: send: %m");
    close(s);
    return -1;
  }

  close(s);
  return 0;
}

/*
 * Perform setkey(8)-like operation for set the password for TCP MD5 Signature.
 * Could be called with SABD_ADD or SADB_DELETE argument. Note that SADB_ADD
 * argument is internally processed as a pair of SADB_ADD and SADB_DELETE
 * operations to implement replace.
 */
static int
setkey_md5(sockaddr *src, uint slen, sockaddr *dst, uint dlen, char *passwd, uint type)
{
  uint passwd_len = passwd ? strlen(passwd) : 0;

  uint total =
    sizeof(struct sadb_msg) +
    sizeof(struct sadb_key) + PFKEY_ALIGN8(passwd_len) +
    sizeof(struct sadb_sa) +
    sizeof(struct sadb_x_sa2) +
    sizeof(struct sadb_address) + PFKEY_ALIGN8(src->sa.sa_len) +
    sizeof(struct sadb_address) + PFKEY_ALIGN8(dst->sa.sa_len);

  char *buf = alloca(total);
  char *pos = buf;
  uint len;

  memset(buf, 0, total);

  struct sadb_msg *msg = (void *) pos;
  len = sizeof(struct sadb_msg);
  msg->sadb_msg_version = PF_KEY_V2;
  msg->sadb_msg_type = type;
  msg->sadb_msg_satype = SADB_X_SATYPE_TCPSIGNATURE;
  msg->sadb_msg_len = 0;	/* Fix it later */
  msg->sadb_msg_pid = getpid();
  pos += len;

  /* Set authentication algorithm and password */
  struct sadb_key *key = (void *) pos;
  len = sizeof(struct sadb_key) + PFKEY_ALIGN8(passwd_len);
  key->sadb_key_len = PFKEY_UNIT64(len);
  key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
  key->sadb_key_bits = passwd_len * 8;
  memcpy(pos + sizeof(struct sadb_key), passwd, passwd_len);
  pos += len;

  struct sadb_sa *sa = (void *) pos;
  len = sizeof(struct sadb_sa);
  sa->sadb_sa_len = PFKEY_UNIT64(len);
  sa->sadb_sa_exttype = SADB_EXT_SA;
  sa->sadb_sa_spi = htonl((u32) TCP_SIG_SPI);
  sa->sadb_sa_auth = SADB_X_AALG_TCP_MD5;
  sa->sadb_sa_encrypt = SADB_EALG_NONE;
  sa->sadb_sa_flags = SADB_X_EXT_CYCSEQ;
  pos += len;

  struct sadb_x_sa2 *sa2 = (void *) pos;
  len = sizeof(struct sadb_x_sa2);
  sa2->sadb_x_sa2_len = PFKEY_UNIT64(len);
  sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
  sa2->sadb_x_sa2_mode = IPSEC_MODE_ANY;
  pos += len;

  /* Set source address */
  struct sadb_address *saddr = (void *) pos;
  len = sizeof(struct sadb_address) + PFKEY_ALIGN8(src->sa.sa_len);
  saddr->sadb_address_len = PFKEY_UNIT64(len);
  saddr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
  saddr->sadb_address_proto = IPSEC_ULPROTO_ANY;
  saddr->sadb_address_prefixlen = slen;
  memcpy(pos + sizeof(struct sadb_address), &src->sa, src->sa.sa_len);
  pos += len;

  /* Set destination address */
  struct sadb_address *daddr = (void *) pos;
  len = sizeof(struct sadb_address) + PFKEY_ALIGN8(dst->sa.sa_len);
  daddr->sadb_address_len = PFKEY_UNIT64(len);
  daddr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
  daddr->sadb_address_proto = IPSEC_ULPROTO_ANY;
  daddr->sadb_address_prefixlen = dlen;
  memcpy(pos + sizeof(struct sadb_address), &dst->sa, dst->sa.sa_len);
  pos += len;

  len = pos - buf;
  msg->sadb_msg_len = PFKEY_UNIT64(len);

  return setkey_send(msg, len);
}

/*
 * Manipulation with the IPsec SA/SP database
 */
static int
sk_set_md5_in_sasp_db(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, char *passwd)
{
  sockaddr src, dst;
  sockaddr_fill(&src, s->af, local, ifa, 0);
  sockaddr_fill(&dst, s->af, remote, ifa, 0);

  uint maxlen = (s->af == AF_INET) ? IP4_MAX_PREFIX_LENGTH : IP6_MAX_PREFIX_LENGTH;
  uint slen = maxlen;
  uint dlen = (pxlen < 0) ? maxlen : pxlen;

  if (passwd && *passwd)
  {
    int len = strlen(passwd);
    if (len > TCP_KEYLEN_MAX)
      ERR_MSG("The password for TCP MD5 Signature is too long");

    if ((setkey_md5(&src, slen, &dst, dlen, passwd, SADB_ADD) < 0) ||
	(setkey_md5(&dst, dlen, &src, slen, passwd, SADB_ADD) < 0))
      ERR_MSG("Cannot add TCP-MD5 password into the IPsec SA/SP database");
  }
  else
  {
    if ((setkey_md5(&src, slen, &dst, dlen, NULL, SADB_DELETE) < 0) ||
	(setkey_md5(&dst, dlen, &src, slen, NULL, SADB_DELETE) < 0))
      ERR_MSG("Cannot delete TCP-MD5 password from the IPsec SA/SP database");
  }
  return 0;
}