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 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 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 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