1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2017 Joyent, Inc.
14 */
15
16 #include <sys/socket.h>
17 #include <net/pfkeyv2.h>
18 #include <stdio.h>
19 #include <errno.h>
20 #include <err.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ipsec_util.h>
25
26 #define COOKIE64 0xc0ffee4afee01deaULL
27 #define COOKIE32 0x90125
28 #define RESERVED 0xc0ffee
29
30 #define EXIT_SETUP_FAIL -1
31 #define EXIT_TEST_FAIL 1
32 #define EXIT_SUCCESS 0
33
34 /*
35 * Exits app on failure.
36 */
37 static void
write_and_read(int s,sadb_msg_t * samsg,uint64_t * readbuf,int readlen,int expected,char * msgtypestr)38 write_and_read(int s, sadb_msg_t *samsg, uint64_t *readbuf, int readlen,
39 int expected, char *msgtypestr)
40 {
41 ssize_t rc;
42 uint8_t msgtype = samsg->sadb_msg_type;
43 pid_t pid = samsg->sadb_msg_pid;
44 uint8_t seq = samsg->sadb_msg_seq;
45
46 rc = write(s, samsg, SADB_64TO8(samsg->sadb_msg_len));
47 if (rc == -1)
48 err(EXIT_SETUP_FAIL, "%s write error", msgtypestr);
49
50 /* Yes, parameter re-use, but we're done writing. */
51 samsg = (sadb_msg_t *)readbuf;
52 do {
53 rc = read(s, readbuf, readlen);
54 if (rc == -1)
55 err(EXIT_SETUP_FAIL, "%s read reply error", msgtypestr);
56 } while (samsg->sadb_msg_seq != seq || samsg->sadb_msg_pid != pid ||
57 samsg->sadb_msg_type != msgtype);
58
59 if (samsg->sadb_msg_errno != expected) {
60 errno = samsg->sadb_msg_errno;
61 err(EXIT_SETUP_FAIL, "%s reply has error (diag = %d, %s)",
62 msgtypestr, samsg->sadb_x_msg_diagnostic,
63 keysock_diag(samsg->sadb_x_msg_diagnostic));
64 }
65 }
66
67 static void
usage(const char * progname)68 usage(const char *progname)
69 {
70 (void) fprintf(stderr, "Usage: %s [-e expected_error] [-k kmc_value] "
71 "[-p kmc_proto] <spi-value> [64]\n", progname);
72 exit(EXIT_SETUP_FAIL);
73 }
74
75 int
main(int argc,char * const argv[])76 main(int argc, char * const argv[])
77 {
78 uint32_t spi;
79 sadb_ext_t *ext;
80 sadb_sa_t *saext;
81 sadb_msg_t *samsg;
82 sadb_address_t *dstext, *srcext;
83 sadb_x_kmc_t *kmcext;
84 struct sockaddr_in *sin;
85 uint64_t writebuf[20]; /* PF_KEY likes 64-bit alignment. */
86 uint64_t readbuf[128];
87 uint64_t *extptr, *endptr;
88 const char *cookiestr = NULL;
89 uint64_t cookie64 = COOKIE64;
90 uint32_t cookie32 = COOKIE32;
91 uint32_t reserved = RESERVED;
92 uint32_t proto = 0;
93 int experr = 0;
94 pid_t pid = getpid();
95 boolean_t do_64_test;
96 int s;
97 int c;
98
99 while ((c = getopt(argc, argv, "e:k:p:")) != -1) {
100 switch (c) {
101 case 'e':
102 errno = 0;
103 experr = strtol(optarg, NULL, 0);
104 if (errno != 0) {
105 err(EXIT_SETUP_FAIL,
106 "Expected error value '%s' is not a "
107 "parsable number", optarg);
108 }
109 break;
110 case 'k':
111 cookiestr = optarg;
112 break;
113 case 'p':
114 errno = 0;
115 proto = strtoul(optarg, NULL, 0);
116 if (errno != 0) {
117 err(EXIT_SETUP_FAIL,
118 "KMC Protocol value '%s' is not a parsable"
119 " number", optarg);
120 }
121 break;
122 case '?':
123 (void) fprintf(stderr, "Invalid option -%c\n", optopt);
124 usage(argv[0]);
125 break;
126 }
127 }
128
129 if (argc - optind != 1 && argc - optind != 2)
130 usage(argv[0]);
131
132 do_64_test = (argc - optind == 2);
133
134 if (cookiestr != NULL) {
135 errno = 0;
136
137 if (do_64_test)
138 cookie64 = strtoull(cookiestr, NULL, 0);
139 else
140 cookie32 = strtoul(cookiestr, NULL, 0);
141
142 if (errno != 0) {
143 err(EXIT_SETUP_FAIL,
144 "KMC '%s' is not a parsable number",
145 cookiestr);
146 }
147 }
148
149 if (proto == 0)
150 proto = do_64_test ? SADB_X_KMP_KINK : SADB_X_KMP_IKE;
151
152 errno = 0; /* Clear for strtoul() call. */
153 spi = strtoul(argv[optind], NULL, 0);
154 if (spi == 0) {
155 if (errno != 0) {
156 err(EXIT_SETUP_FAIL,
157 "Argument %s is not a parsable number:", argv[1]);
158 } else {
159 errno = EINVAL;
160 err(EXIT_SETUP_FAIL, "Zero SPI not allowed:");
161 }
162 }
163
164 s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
165 if (s == -1)
166 err(EXIT_SETUP_FAIL, "socket(PF_KEY)");
167
168 /* Base message. */
169 samsg = (sadb_msg_t *)writebuf;
170 samsg->sadb_msg_version = PF_KEY_V2;
171 samsg->sadb_msg_type = SADB_UPDATE;
172 samsg->sadb_msg_errno = 0;
173 samsg->sadb_msg_satype = SADB_SATYPE_AH;
174 samsg->sadb_msg_reserved = 0;
175 samsg->sadb_msg_seq = 1;
176 samsg->sadb_msg_pid = pid;
177 samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg) + sizeof (*saext) +
178 2 * (sizeof (*dstext) + sizeof (*sin)) + sizeof (*kmcext));
179
180 /* SA extension. Only used to set the SPI. */
181 saext = (sadb_sa_t *)(samsg + 1);
182 memset(saext, 0, sizeof (*saext));
183 saext->sadb_sa_len = SADB_8TO64(sizeof (*saext));
184 saext->sadb_sa_exttype = SADB_EXT_SA;
185 saext->sadb_sa_spi = htonl(spi);
186 saext->sadb_sa_state = SADB_SASTATE_MATURE;
187
188 /* Destination IP, always 127.0.0.1 for this test. */
189 dstext = (sadb_address_t *)(saext + 1);
190 dstext->sadb_address_len = SADB_8TO64(sizeof (*dstext) + sizeof (*sin));
191 dstext->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
192 dstext->sadb_address_proto = 0;
193 dstext->sadb_address_prefixlen = 0;
194 dstext->sadb_address_reserved = 0;
195 sin = (struct sockaddr_in *)(dstext + 1);
196 sin->sin_family = AF_INET;
197 sin->sin_port = 0;
198 sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
199
200 /* PF_KEY requires a source address, even if it's a wildcard. */
201 srcext = (sadb_address_t *)(sin + 1);
202 srcext->sadb_address_len = SADB_8TO64(sizeof (*srcext) + sizeof (*sin));
203 srcext->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
204 srcext->sadb_address_proto = 0;
205 srcext->sadb_address_prefixlen = 0;
206 srcext->sadb_address_reserved = 0;
207 sin = (struct sockaddr_in *)(srcext + 1);
208 sin->sin_family = AF_INET;
209 sin->sin_port = 0;
210 sin->sin_addr.s_addr = 0;
211
212 /*
213 * KM cookie. Depending, either make it IKEv1, 32-bit, AND store
214 * garbage in the reserved field, or just put a big 64-bit cookie in.
215 */
216 kmcext = (sadb_x_kmc_t *)(sin + 1);
217 kmcext->sadb_x_kmc_len = SADB_8TO64(sizeof (*kmcext));
218 kmcext->sadb_x_kmc_exttype = SADB_X_EXT_KM_COOKIE;
219 if (do_64_test) {
220 /* 64-bit cookie test. KINK is non-zero, and non-IKEv1. */
221 kmcext->sadb_x_kmc_proto = proto;
222 kmcext->sadb_x_kmc_cookie64 = cookie64;
223 } else {
224 /* IKEv1 32-bit cookie test. */
225 kmcext->sadb_x_kmc_proto = proto;
226 kmcext->sadb_x_kmc_cookie = cookie32;
227 kmcext->sadb_x_kmc_reserved = reserved;
228 }
229
230 write_and_read(s, samsg, readbuf, sizeof (readbuf), experr,
231 "SADB_UPDATE");
232
233 /* If we expected to fail, we shouldn't try to verify anything */
234 if (experr != 0)
235 exit(EXIT_SUCCESS);
236
237 /*
238 * Okay, it worked! Now let's find the KMC reported back from the
239 * kernel.
240 */
241 samsg->sadb_msg_type = SADB_GET;
242 samsg->sadb_msg_len -= SADB_8TO64(sizeof (*kmcext));
243
244 /* Everything else in writebuf is good to go. */
245 write_and_read(s, samsg, readbuf, sizeof (readbuf), 0, "SADB_GET");
246
247 /* Actually find the KMC extension. (expand for loop for readability) */
248 samsg = (sadb_msg_t *)readbuf;
249 extptr = (uint64_t *)(samsg + 1);
250 endptr = extptr + samsg->sadb_msg_len - SADB_8TO64(sizeof (*samsg));
251 ext = (sadb_ext_t *)extptr;
252
253 while ((extptr < endptr) &&
254 (ext->sadb_ext_type != SADB_X_EXT_KM_COOKIE)) {
255 extptr += ext->sadb_ext_len;
256 ext = (sadb_ext_t *)extptr;
257 }
258
259 if (extptr == endptr) {
260 (void) fprintf(stderr, "Can't find KMC extension in reply.\n");
261 exit(EXIT_SETUP_FAIL);
262 }
263 kmcext = (sadb_x_kmc_t *)extptr;
264
265 if (do_64_test) {
266 if (kmcext->sadb_x_kmc_proto != proto ||
267 kmcext->sadb_x_kmc_cookie64 != cookie64) {
268 (void) fprintf(stderr, "Unexpected 64-bit results: "
269 "KMC received was %" PRIu32
270 ", expecting %" PRIu32 ",\n",
271 kmcext->sadb_x_kmc_proto, proto);
272 (void) fprintf(stderr, "64-bit cookie recevied was "
273 "0x%"PRIx64", expecting 0x%"PRIx64"\n",
274 kmcext->sadb_x_kmc_cookie64, cookie64);
275 exit(EXIT_TEST_FAIL);
276 }
277 } else {
278 if (kmcext->sadb_x_kmc_proto != proto ||
279 kmcext->sadb_x_kmc_cookie != cookie32 ||
280 kmcext->sadb_x_kmc_reserved != 0) {
281 (void) fprintf(stderr, "Unexpected IKE/32-bit results:"
282 " KMC received was %" PRIu32
283 ", expecting %" PRIu32 ",\n",
284 kmcext->sadb_x_kmc_proto, proto);
285 (void) fprintf(stderr, "32-bit cookie recevied was "
286 "0x%"PRIx32", expecting 0x%"PRIx32"\n",
287 kmcext->sadb_x_kmc_cookie64, cookie32);
288 (void) fprintf(stderr, "32-bit reserved recevied was "
289 "0x%"PRIx32", expecting 0\n",
290 kmcext->sadb_x_kmc_cookie64);
291 exit(EXIT_TEST_FAIL);
292 }
293 }
294
295 exit(EXIT_SUCCESS);
296 }
297