11edba515SAndy Fiddaman /*
21edba515SAndy Fiddaman * This file and its contents are supplied under the terms of the
31edba515SAndy Fiddaman * Common Development and Distribution License ("CDDL"), version 1.0.
41edba515SAndy Fiddaman * You may only use this file in accordance with the terms of version
51edba515SAndy Fiddaman * 1.0 of the CDDL.
61edba515SAndy Fiddaman *
71edba515SAndy Fiddaman * A full copy of the text of the CDDL should have accompanied this
81edba515SAndy Fiddaman * source. A copy of the CDDL is also available via the Internet at
91edba515SAndy Fiddaman * http://www.illumos.org/license/CDDL.
101edba515SAndy Fiddaman */
111edba515SAndy Fiddaman
121edba515SAndy Fiddaman /*
131edba515SAndy Fiddaman * Copyright 2024 Oxide Computer Company
141edba515SAndy Fiddaman */
151edba515SAndy Fiddaman
161edba515SAndy Fiddaman /*
171edba515SAndy Fiddaman * RFC 2385 TCP MD5 Signature Option
181edba515SAndy Fiddaman *
191edba515SAndy Fiddaman * A security option commonly used to enhance security for BGP sessions. When a
201edba515SAndy Fiddaman * TCP socket has its TCP_MD5SIG option enabled, an additional TCP option is
211edba515SAndy Fiddaman * added to the header containing an MD5 digest calculated across the pseudo IP
221edba515SAndy Fiddaman * header, part of the TCP header, the data in the segment and a shared secret.
231edba515SAndy Fiddaman * The option is large (18 bytes plus 2 more for padding to a word boundary),
241edba515SAndy Fiddaman * and often /just/ fits in the TCP header -- particularly with SYN packets due
251edba515SAndy Fiddaman * to their additional options such as MSS.
261edba515SAndy Fiddaman *
271edba515SAndy Fiddaman * The socket option is boolean, and it is also necessary to have configured a
281edba515SAndy Fiddaman * security association (SA) to match the traffic that should be signed, and to
291edba515SAndy Fiddaman * provide the signing key. These SAs are configured from userland via
301edba515SAndy Fiddaman * tcpkey(8), use source and destination addresses and ports as criteria, and
311edba515SAndy Fiddaman * are maintained in a per-netstack linked list. The SAs pertaining to a
321edba515SAndy Fiddaman * particular TCP connection, one for each direction, are cached in the
331edba515SAndy Fiddaman * connection's TCP state after the first packet has been processed, and so
341edba515SAndy Fiddaman * using a single list is not a significant overhead, particularly as it is
351edba515SAndy Fiddaman * expected to be short.
361edba515SAndy Fiddaman *
371edba515SAndy Fiddaman * Enabling the socket option has a number of side effects:
381edba515SAndy Fiddaman *
391edba515SAndy Fiddaman * - TCP fast path is disabled;
401edba515SAndy Fiddaman * - TCP Fusion is disabled;
411edba515SAndy Fiddaman * - Outbound packets for which a matching SA cannot be found are silently
421edba515SAndy Fiddaman * discarded.
431edba515SAndy Fiddaman * - Inbound packets that DO NOT contain an MD5 option in their TCP header are
441edba515SAndy Fiddaman * silently discarded.
451edba515SAndy Fiddaman * - Inbound packets that DO contain an MD5 option but for which the digest
461edba515SAndy Fiddaman * does not match the locally calculated one are silently discarded.
471edba515SAndy Fiddaman *
481edba515SAndy Fiddaman * An SA is bound to a TCP stream once the first packet is sent or received
491edba515SAndy Fiddaman * following the TCP_MD5SIG socket option being enabled. Typically an
501edba515SAndy Fiddaman * application will enable the socket option immediately after creating the
511edba515SAndy Fiddaman * socket, and before moving on to calling connect() or bind() but it is
521edba515SAndy Fiddaman * necessary to wait for the first packet as that is the point at which the
531edba515SAndy Fiddaman * source and destination addresses and ports are all known, and we need these
541edba515SAndy Fiddaman * to find the SA. Note that if no matching SA is present in the database when
551edba515SAndy Fiddaman * the first packet is sent or received, it will be silently dropped. Due to
561edba515SAndy Fiddaman * the reference counting and tombstone logic, an SA that has been bound to one
571edba515SAndy Fiddaman * or more streams will persist until all of those streams have been torn down.
581edba515SAndy Fiddaman * It is not possible to change the SA for an active connection.
591edba515SAndy Fiddaman *
601edba515SAndy Fiddaman * -------------
611edba515SAndy Fiddaman * Lock Ordering
621edba515SAndy Fiddaman * -------------
631edba515SAndy Fiddaman *
641edba515SAndy Fiddaman * In order to ensure that we don't deadlock, if both are required, the RW lock
651edba515SAndy Fiddaman * across the SADB must be taken before acquiring an individual SA's lock. That
661edba515SAndy Fiddaman * is, locks must be taken in the following order (and released in the opposite
671edba515SAndy Fiddaman * order):
681edba515SAndy Fiddaman *
691edba515SAndy Fiddaman * 0) <tcpstack>->tcps_sigdb->td_lock
701edba515SAndy Fiddaman * 1) <tcpstack>->tcps_sigdb->td_sa.list-><entry>->ts_lock
711edba515SAndy Fiddaman *
721edba515SAndy Fiddaman * The lock at <tcpstack>->tcps_sigdb_lock is independent and used to
731edba515SAndy Fiddaman * synchronize lazy initialization of the database.
741edba515SAndy Fiddaman */
751edba515SAndy Fiddaman
761edba515SAndy Fiddaman #include <sys/atomic.h>
771edba515SAndy Fiddaman #include <sys/cmn_err.h>
781edba515SAndy Fiddaman #include <sys/cpuvar.h>
791edba515SAndy Fiddaman #include <sys/debug.h>
801edba515SAndy Fiddaman #include <sys/errno.h>
811edba515SAndy Fiddaman #include <sys/kmem.h>
821edba515SAndy Fiddaman #include <sys/list.h>
831edba515SAndy Fiddaman #include <sys/md5.h>
841edba515SAndy Fiddaman #include <sys/stdbool.h>
851edba515SAndy Fiddaman #include <sys/stream.h>
861edba515SAndy Fiddaman #include <sys/stropts.h>
871edba515SAndy Fiddaman #include <sys/strsubr.h>
881edba515SAndy Fiddaman #include <sys/strsun.h>
891edba515SAndy Fiddaman #include <sys/sysmacros.h>
901edba515SAndy Fiddaman #include <sys/types.h>
911edba515SAndy Fiddaman #include <netinet/in.h>
921edba515SAndy Fiddaman #include <netinet/ip6.h>
931edba515SAndy Fiddaman #include <net/pfkeyv2.h>
941edba515SAndy Fiddaman #include <net/pfpolicy.h>
951edba515SAndy Fiddaman #include <inet/common.h>
961edba515SAndy Fiddaman #include <inet/mi.h>
971edba515SAndy Fiddaman #include <inet/ip.h>
981edba515SAndy Fiddaman #include <inet/ip6.h>
991edba515SAndy Fiddaman #include <inet/ip_if.h>
1001edba515SAndy Fiddaman #include <inet/tcp_stats.h>
1011edba515SAndy Fiddaman #include <inet/keysock.h>
1021edba515SAndy Fiddaman #include <inet/sadb.h>
1031edba515SAndy Fiddaman #include <inet/tcp_sig.h>
1041edba515SAndy Fiddaman
1051edba515SAndy Fiddaman static void tcpsig_sa_free(tcpsig_sa_t *);
1061edba515SAndy Fiddaman
1071edba515SAndy Fiddaman void
tcpsig_init(tcp_stack_t * tcps)1081edba515SAndy Fiddaman tcpsig_init(tcp_stack_t *tcps)
1091edba515SAndy Fiddaman {
1101edba515SAndy Fiddaman mutex_init(&tcps->tcps_sigdb_lock, NULL, MUTEX_DEFAULT, NULL);
1111edba515SAndy Fiddaman }
1121edba515SAndy Fiddaman
1131edba515SAndy Fiddaman void
tcpsig_fini(tcp_stack_t * tcps)1141edba515SAndy Fiddaman tcpsig_fini(tcp_stack_t *tcps)
1151edba515SAndy Fiddaman {
1161edba515SAndy Fiddaman tcpsig_db_t *db;
1171edba515SAndy Fiddaman
1181edba515SAndy Fiddaman if ((db = tcps->tcps_sigdb) != NULL) {
1191edba515SAndy Fiddaman tcpsig_sa_t *sa;
1201edba515SAndy Fiddaman
1211edba515SAndy Fiddaman rw_destroy(&db->td_lock);
1221edba515SAndy Fiddaman while ((sa = list_remove_head(&db->td_salist)) != NULL)
1231edba515SAndy Fiddaman tcpsig_sa_free(sa);
1241edba515SAndy Fiddaman list_destroy(&db->td_salist);
1251edba515SAndy Fiddaman kmem_free(tcps->tcps_sigdb, sizeof (tcpsig_db_t));
1261edba515SAndy Fiddaman tcps->tcps_sigdb = NULL;
1271edba515SAndy Fiddaman }
1281edba515SAndy Fiddaman mutex_destroy(&tcps->tcps_sigdb_lock);
1291edba515SAndy Fiddaman }
1301edba515SAndy Fiddaman
1311edba515SAndy Fiddaman static tcpsig_db_t *
tcpsig_db(tcp_stack_t * tcps)1321edba515SAndy Fiddaman tcpsig_db(tcp_stack_t *tcps)
1331edba515SAndy Fiddaman {
1341edba515SAndy Fiddaman mutex_enter(&tcps->tcps_sigdb_lock);
1351edba515SAndy Fiddaman if (tcps->tcps_sigdb == NULL) {
1361edba515SAndy Fiddaman tcpsig_db_t *db = kmem_alloc(sizeof (tcpsig_db_t), KM_SLEEP);
1371edba515SAndy Fiddaman
1381edba515SAndy Fiddaman rw_init(&db->td_lock, NULL, RW_DEFAULT, 0);
1391edba515SAndy Fiddaman list_create(&db->td_salist, sizeof (tcpsig_sa_t),
1401edba515SAndy Fiddaman offsetof(tcpsig_sa_t, ts_link));
1411edba515SAndy Fiddaman
1421edba515SAndy Fiddaman tcps->tcps_sigdb = db;
1431edba515SAndy Fiddaman }
1441edba515SAndy Fiddaman mutex_exit(&tcps->tcps_sigdb_lock);
1451edba515SAndy Fiddaman
1461edba515SAndy Fiddaman return ((tcpsig_db_t *)tcps->tcps_sigdb);
1471edba515SAndy Fiddaman }
1481edba515SAndy Fiddaman
149*c2cbc6b8SAndy Fiddaman static uint8_t *
tcpsig_make_sa_ext(uint8_t * start,const uint8_t * const end,const tcpsig_sa_t * sa)150*c2cbc6b8SAndy Fiddaman tcpsig_make_sa_ext(uint8_t *start, const uint8_t * const end,
151*c2cbc6b8SAndy Fiddaman const tcpsig_sa_t *sa)
152*c2cbc6b8SAndy Fiddaman {
153*c2cbc6b8SAndy Fiddaman sadb_sa_t *assoc;
154*c2cbc6b8SAndy Fiddaman
155*c2cbc6b8SAndy Fiddaman ASSERT3P(end, >, start);
156*c2cbc6b8SAndy Fiddaman
157*c2cbc6b8SAndy Fiddaman if (start == NULL || end - start < sizeof (*assoc))
158*c2cbc6b8SAndy Fiddaman return (NULL);
159*c2cbc6b8SAndy Fiddaman
160*c2cbc6b8SAndy Fiddaman assoc = (sadb_sa_t *)start;
161*c2cbc6b8SAndy Fiddaman assoc->sadb_sa_exttype = SADB_EXT_SA;
162*c2cbc6b8SAndy Fiddaman assoc->sadb_sa_len = SADB_8TO64(sizeof (*assoc));
163*c2cbc6b8SAndy Fiddaman assoc->sadb_sa_auth = sa->ts_key.sak_algid;
164*c2cbc6b8SAndy Fiddaman assoc->sadb_sa_flags = SADB_X_SAFLAGS_TCPSIG;
165*c2cbc6b8SAndy Fiddaman assoc->sadb_sa_state = sa->ts_state;
166*c2cbc6b8SAndy Fiddaman
167*c2cbc6b8SAndy Fiddaman return ((uint8_t *)(assoc + 1));
168*c2cbc6b8SAndy Fiddaman }
169*c2cbc6b8SAndy Fiddaman
170*c2cbc6b8SAndy Fiddaman static size_t
tcpsig_addr_extsize(const tcpsig_sa_t * sa)171*c2cbc6b8SAndy Fiddaman tcpsig_addr_extsize(const tcpsig_sa_t *sa)
172*c2cbc6b8SAndy Fiddaman {
173*c2cbc6b8SAndy Fiddaman size_t addrsize = 0;
174*c2cbc6b8SAndy Fiddaman
175*c2cbc6b8SAndy Fiddaman switch (sa->ts_family) {
176*c2cbc6b8SAndy Fiddaman case AF_INET:
177*c2cbc6b8SAndy Fiddaman addrsize = roundup(sizeof (sin_t) +
178*c2cbc6b8SAndy Fiddaman sizeof (sadb_address_t), sizeof (uint64_t));
179*c2cbc6b8SAndy Fiddaman break;
180*c2cbc6b8SAndy Fiddaman case AF_INET6:
181*c2cbc6b8SAndy Fiddaman addrsize = roundup(sizeof (sin6_t) +
182*c2cbc6b8SAndy Fiddaman sizeof (sadb_address_t), sizeof (uint64_t));
183*c2cbc6b8SAndy Fiddaman break;
184*c2cbc6b8SAndy Fiddaman }
185*c2cbc6b8SAndy Fiddaman return (addrsize);
186*c2cbc6b8SAndy Fiddaman }
187*c2cbc6b8SAndy Fiddaman
188*c2cbc6b8SAndy Fiddaman static uint8_t *
tcpsig_make_addr_ext(uint8_t * start,const uint8_t * const end,uint16_t exttype,sa_family_t af,const struct sockaddr_storage * addr)189*c2cbc6b8SAndy Fiddaman tcpsig_make_addr_ext(uint8_t *start, const uint8_t * const end,
190*c2cbc6b8SAndy Fiddaman uint16_t exttype, sa_family_t af, const struct sockaddr_storage *addr)
191*c2cbc6b8SAndy Fiddaman {
192*c2cbc6b8SAndy Fiddaman uint8_t *cur = start;
193*c2cbc6b8SAndy Fiddaman unsigned int addrext_len;
194*c2cbc6b8SAndy Fiddaman sadb_address_t *addrext;
195*c2cbc6b8SAndy Fiddaman
196*c2cbc6b8SAndy Fiddaman ASSERT(af == AF_INET || af == AF_INET6);
197*c2cbc6b8SAndy Fiddaman ASSERT3P(end, >, start);
198*c2cbc6b8SAndy Fiddaman
199*c2cbc6b8SAndy Fiddaman if (cur == NULL)
200*c2cbc6b8SAndy Fiddaman return (NULL);
201*c2cbc6b8SAndy Fiddaman
202*c2cbc6b8SAndy Fiddaman if (end - cur < sizeof (*addrext))
203*c2cbc6b8SAndy Fiddaman return (NULL);
204*c2cbc6b8SAndy Fiddaman
205*c2cbc6b8SAndy Fiddaman addrext = (sadb_address_t *)cur;
206*c2cbc6b8SAndy Fiddaman addrext->sadb_address_proto = IPPROTO_TCP;
207*c2cbc6b8SAndy Fiddaman addrext->sadb_address_reserved = 0;
208*c2cbc6b8SAndy Fiddaman addrext->sadb_address_prefixlen = 0;
209*c2cbc6b8SAndy Fiddaman addrext->sadb_address_exttype = exttype;
210*c2cbc6b8SAndy Fiddaman cur = (uint8_t *)(addrext + 1);
211*c2cbc6b8SAndy Fiddaman
212*c2cbc6b8SAndy Fiddaman if (af == AF_INET) {
213*c2cbc6b8SAndy Fiddaman sin_t *sin;
214*c2cbc6b8SAndy Fiddaman
215*c2cbc6b8SAndy Fiddaman if (end - cur < sizeof (*sin))
216*c2cbc6b8SAndy Fiddaman return (NULL);
217*c2cbc6b8SAndy Fiddaman sin = (sin_t *)cur;
218*c2cbc6b8SAndy Fiddaman
219*c2cbc6b8SAndy Fiddaman *sin = sin_null;
220*c2cbc6b8SAndy Fiddaman bcopy(addr, sin, sizeof (*sin));
221*c2cbc6b8SAndy Fiddaman cur = (uint8_t *)(sin + 1);
222*c2cbc6b8SAndy Fiddaman } else {
223*c2cbc6b8SAndy Fiddaman sin6_t *sin6;
224*c2cbc6b8SAndy Fiddaman
225*c2cbc6b8SAndy Fiddaman if (end - cur < sizeof (*sin6))
226*c2cbc6b8SAndy Fiddaman return (NULL);
227*c2cbc6b8SAndy Fiddaman sin6 = (sin6_t *)cur;
228*c2cbc6b8SAndy Fiddaman
229*c2cbc6b8SAndy Fiddaman *sin6 = sin6_null;
230*c2cbc6b8SAndy Fiddaman bcopy(addr, sin6, sizeof (*sin6));
231*c2cbc6b8SAndy Fiddaman cur = (uint8_t *)(sin6 + 1);
232*c2cbc6b8SAndy Fiddaman }
233*c2cbc6b8SAndy Fiddaman
234*c2cbc6b8SAndy Fiddaman addrext_len = roundup(cur - start, sizeof (uint64_t));
235*c2cbc6b8SAndy Fiddaman addrext->sadb_address_len = SADB_8TO64(addrext_len);
236*c2cbc6b8SAndy Fiddaman
237*c2cbc6b8SAndy Fiddaman if (end - start < addrext_len)
238*c2cbc6b8SAndy Fiddaman return (NULL);
239*c2cbc6b8SAndy Fiddaman return (start + addrext_len);
240*c2cbc6b8SAndy Fiddaman }
241*c2cbc6b8SAndy Fiddaman
242*c2cbc6b8SAndy Fiddaman #define SET_EXPIRE(sa, delta, exp) do { \
243*c2cbc6b8SAndy Fiddaman if (((sa)->ts_ ## delta) != 0) { \
244*c2cbc6b8SAndy Fiddaman (sa)->ts_ ## exp = tcpsig_add_time((sa)->ts_addtime, \
245*c2cbc6b8SAndy Fiddaman (sa)->ts_ ## delta); \
246*c2cbc6b8SAndy Fiddaman } \
247*c2cbc6b8SAndy Fiddaman } while (0)
248*c2cbc6b8SAndy Fiddaman
249*c2cbc6b8SAndy Fiddaman #define UPDATE_EXPIRE(sa, delta, exp) do { \
250*c2cbc6b8SAndy Fiddaman if (((sa)->ts_ ## delta) != 0) { \
251*c2cbc6b8SAndy Fiddaman time_t tmp = tcpsig_add_time((sa)->ts_usetime, \
252*c2cbc6b8SAndy Fiddaman (sa)->ts_ ## delta); \
253*c2cbc6b8SAndy Fiddaman if (((sa)->ts_ ## exp) == 0) \
254*c2cbc6b8SAndy Fiddaman (sa)->ts_ ## exp = tmp; \
255*c2cbc6b8SAndy Fiddaman else \
256*c2cbc6b8SAndy Fiddaman (sa)->ts_ ## exp = MIN((sa)->ts_ ## exp, tmp); \
257*c2cbc6b8SAndy Fiddaman } \
258*c2cbc6b8SAndy Fiddaman } while (0)
259*c2cbc6b8SAndy Fiddaman
260*c2cbc6b8SAndy Fiddaman #define EXPIRED(sa, exp, now) \
261*c2cbc6b8SAndy Fiddaman ((sa)->ts_ ## exp != 0 && sa->ts_ ## exp < (now))
262*c2cbc6b8SAndy Fiddaman
263*c2cbc6b8SAndy Fiddaman /*
264*c2cbc6b8SAndy Fiddaman * PF_KEY gives us lifetimes in uint64_t seconds. In order to avoid odd
265*c2cbc6b8SAndy Fiddaman * behaviour (either negative lifetimes or loss of high order bits) when
266*c2cbc6b8SAndy Fiddaman * someone asks for bizarrely long SA lifetimes, we do a saturating add for
267*c2cbc6b8SAndy Fiddaman * expire times.
268*c2cbc6b8SAndy Fiddaman */
269*c2cbc6b8SAndy Fiddaman #define TIME_MAX INT64_MAX
270*c2cbc6b8SAndy Fiddaman static time_t
tcpsig_add_time(time_t base,uint64_t delta)271*c2cbc6b8SAndy Fiddaman tcpsig_add_time(time_t base, uint64_t delta)
272*c2cbc6b8SAndy Fiddaman {
273*c2cbc6b8SAndy Fiddaman if (delta > TIME_MAX)
274*c2cbc6b8SAndy Fiddaman delta = TIME_MAX;
275*c2cbc6b8SAndy Fiddaman
276*c2cbc6b8SAndy Fiddaman if (base > 0) {
277*c2cbc6b8SAndy Fiddaman if (TIME_MAX - base < delta)
278*c2cbc6b8SAndy Fiddaman return (TIME_MAX);
279*c2cbc6b8SAndy Fiddaman }
280*c2cbc6b8SAndy Fiddaman
281*c2cbc6b8SAndy Fiddaman return (base + delta);
282*c2cbc6b8SAndy Fiddaman }
283*c2cbc6b8SAndy Fiddaman
284*c2cbc6b8SAndy Fiddaman /*
285*c2cbc6b8SAndy Fiddaman * Check hard/soft liftimes and return an appropriate error.
286*c2cbc6b8SAndy Fiddaman */
287*c2cbc6b8SAndy Fiddaman static int
tcpsig_check_lifetimes(sadb_lifetime_t * hard,sadb_lifetime_t * soft)288*c2cbc6b8SAndy Fiddaman tcpsig_check_lifetimes(sadb_lifetime_t *hard, sadb_lifetime_t *soft)
289*c2cbc6b8SAndy Fiddaman {
290*c2cbc6b8SAndy Fiddaman if (hard == NULL || soft == NULL)
291*c2cbc6b8SAndy Fiddaman return (SADB_X_DIAGNOSTIC_NONE);
292*c2cbc6b8SAndy Fiddaman
293*c2cbc6b8SAndy Fiddaman if (hard->sadb_lifetime_addtime != 0 &&
294*c2cbc6b8SAndy Fiddaman soft->sadb_lifetime_addtime != 0 &&
295*c2cbc6b8SAndy Fiddaman hard->sadb_lifetime_addtime < soft->sadb_lifetime_addtime) {
296*c2cbc6b8SAndy Fiddaman return (SADB_X_DIAGNOSTIC_ADDTIME_HSERR);
297*c2cbc6b8SAndy Fiddaman }
298*c2cbc6b8SAndy Fiddaman
299*c2cbc6b8SAndy Fiddaman if (hard->sadb_lifetime_usetime != 0 &&
300*c2cbc6b8SAndy Fiddaman soft->sadb_lifetime_usetime != 0 &&
301*c2cbc6b8SAndy Fiddaman hard->sadb_lifetime_usetime < soft->sadb_lifetime_usetime) {
302*c2cbc6b8SAndy Fiddaman return (SADB_X_DIAGNOSTIC_USETIME_HSERR);
303*c2cbc6b8SAndy Fiddaman }
304*c2cbc6b8SAndy Fiddaman
305*c2cbc6b8SAndy Fiddaman return (SADB_X_DIAGNOSTIC_NONE);
306*c2cbc6b8SAndy Fiddaman }
307*c2cbc6b8SAndy Fiddaman
308*c2cbc6b8SAndy Fiddaman /*
309*c2cbc6b8SAndy Fiddaman * Update the lifetime values of an SA.
310*c2cbc6b8SAndy Fiddaman * If the updated lifetimes mean that a previously dying or dead SA should be
311*c2cbc6b8SAndy Fiddaman * promoted back to mature, then do that too. However, if they would mean that
312*c2cbc6b8SAndy Fiddaman * the SA is immediately expired, then that will be handled on the next
313*c2cbc6b8SAndy Fiddaman * aging run.
314*c2cbc6b8SAndy Fiddaman */
315*c2cbc6b8SAndy Fiddaman static void
tcpsig_update_lifetimes(tcpsig_sa_t * sa,sadb_lifetime_t * hard,sadb_lifetime_t * soft)316*c2cbc6b8SAndy Fiddaman tcpsig_update_lifetimes(tcpsig_sa_t *sa, sadb_lifetime_t *hard,
317*c2cbc6b8SAndy Fiddaman sadb_lifetime_t *soft)
318*c2cbc6b8SAndy Fiddaman {
319*c2cbc6b8SAndy Fiddaman const time_t now = gethrestime_sec();
320*c2cbc6b8SAndy Fiddaman
321*c2cbc6b8SAndy Fiddaman mutex_enter(&sa->ts_lock);
322*c2cbc6b8SAndy Fiddaman
323*c2cbc6b8SAndy Fiddaman if (hard != NULL) {
324*c2cbc6b8SAndy Fiddaman if (hard->sadb_lifetime_usetime != 0)
325*c2cbc6b8SAndy Fiddaman sa->ts_harduselt = hard->sadb_lifetime_usetime;
326*c2cbc6b8SAndy Fiddaman if (hard->sadb_lifetime_addtime != 0)
327*c2cbc6b8SAndy Fiddaman sa->ts_hardaddlt = hard->sadb_lifetime_addtime;
328*c2cbc6b8SAndy Fiddaman if (sa->ts_hardaddlt != 0)
329*c2cbc6b8SAndy Fiddaman SET_EXPIRE(sa, hardaddlt, hardexpiretime);
330*c2cbc6b8SAndy Fiddaman if (sa->ts_harduselt != 0 && sa->ts_usetime != 0)
331*c2cbc6b8SAndy Fiddaman UPDATE_EXPIRE(sa, harduselt, hardexpiretime);
332*c2cbc6b8SAndy Fiddaman if (sa->ts_state == SADB_SASTATE_DEAD &&
333*c2cbc6b8SAndy Fiddaman !EXPIRED(sa, hardexpiretime, now)) {
334*c2cbc6b8SAndy Fiddaman sa->ts_state = SADB_SASTATE_MATURE;
335*c2cbc6b8SAndy Fiddaman }
336*c2cbc6b8SAndy Fiddaman }
337*c2cbc6b8SAndy Fiddaman
338*c2cbc6b8SAndy Fiddaman if (soft != NULL) {
339*c2cbc6b8SAndy Fiddaman if (soft->sadb_lifetime_usetime != 0) {
340*c2cbc6b8SAndy Fiddaman sa->ts_softuselt = MIN(sa->ts_harduselt,
341*c2cbc6b8SAndy Fiddaman soft->sadb_lifetime_usetime);
342*c2cbc6b8SAndy Fiddaman }
343*c2cbc6b8SAndy Fiddaman if (soft->sadb_lifetime_addtime != 0) {
344*c2cbc6b8SAndy Fiddaman sa->ts_softaddlt = MIN(sa->ts_hardaddlt,
345*c2cbc6b8SAndy Fiddaman soft->sadb_lifetime_addtime);
346*c2cbc6b8SAndy Fiddaman }
347*c2cbc6b8SAndy Fiddaman if (sa->ts_softaddlt != 0)
348*c2cbc6b8SAndy Fiddaman SET_EXPIRE(sa, softaddlt, softexpiretime);
349*c2cbc6b8SAndy Fiddaman if (sa->ts_softuselt != 0 && sa->ts_usetime != 0)
350*c2cbc6b8SAndy Fiddaman UPDATE_EXPIRE(sa, softuselt, softexpiretime);
351*c2cbc6b8SAndy Fiddaman if (sa->ts_state == SADB_SASTATE_DYING &&
352*c2cbc6b8SAndy Fiddaman !EXPIRED(sa, softexpiretime, now)) {
353*c2cbc6b8SAndy Fiddaman sa->ts_state = SADB_SASTATE_MATURE;
354*c2cbc6b8SAndy Fiddaman }
355*c2cbc6b8SAndy Fiddaman }
356*c2cbc6b8SAndy Fiddaman
357*c2cbc6b8SAndy Fiddaman mutex_exit(&sa->ts_lock);
358*c2cbc6b8SAndy Fiddaman }
359*c2cbc6b8SAndy Fiddaman
360*c2cbc6b8SAndy Fiddaman static void
tcpsig_sa_touch(tcpsig_sa_t * sa)361*c2cbc6b8SAndy Fiddaman tcpsig_sa_touch(tcpsig_sa_t *sa)
362*c2cbc6b8SAndy Fiddaman {
363*c2cbc6b8SAndy Fiddaman const time_t now = gethrestime_sec();
364*c2cbc6b8SAndy Fiddaman
365*c2cbc6b8SAndy Fiddaman mutex_enter(&sa->ts_lock);
366*c2cbc6b8SAndy Fiddaman sa->ts_lastuse = now;
367*c2cbc6b8SAndy Fiddaman
368*c2cbc6b8SAndy Fiddaman if (sa->ts_usetime == 0) {
369*c2cbc6b8SAndy Fiddaman sa->ts_usetime = now;
370*c2cbc6b8SAndy Fiddaman /* Update expiry times following the first use */
371*c2cbc6b8SAndy Fiddaman UPDATE_EXPIRE(sa, softuselt, softexpiretime);
372*c2cbc6b8SAndy Fiddaman UPDATE_EXPIRE(sa, harduselt, hardexpiretime);
373*c2cbc6b8SAndy Fiddaman }
374*c2cbc6b8SAndy Fiddaman mutex_exit(&sa->ts_lock);
375*c2cbc6b8SAndy Fiddaman }
376*c2cbc6b8SAndy Fiddaman
377*c2cbc6b8SAndy Fiddaman static void
tcpsig_sa_expiremsg(keysock_t * ks,const tcpsig_sa_t * sa,int ltt)378*c2cbc6b8SAndy Fiddaman tcpsig_sa_expiremsg(keysock_t *ks, const tcpsig_sa_t *sa, int ltt)
379*c2cbc6b8SAndy Fiddaman {
380*c2cbc6b8SAndy Fiddaman size_t alloclen;
381*c2cbc6b8SAndy Fiddaman sadb_sa_t *assoc;
382*c2cbc6b8SAndy Fiddaman sadb_msg_t *samsg;
383*c2cbc6b8SAndy Fiddaman sadb_lifetime_t *lt;
384*c2cbc6b8SAndy Fiddaman uint8_t *cur, *end;
385*c2cbc6b8SAndy Fiddaman mblk_t *mp;
386*c2cbc6b8SAndy Fiddaman
387*c2cbc6b8SAndy Fiddaman alloclen = sizeof (sadb_msg_t) + sizeof (sadb_sa_t) +
388*c2cbc6b8SAndy Fiddaman 2 * sizeof (sadb_lifetime_t) + 2 * tcpsig_addr_extsize(sa);
389*c2cbc6b8SAndy Fiddaman
390*c2cbc6b8SAndy Fiddaman mp = allocb(alloclen, BPRI_HI);
391*c2cbc6b8SAndy Fiddaman if (mp == NULL)
392*c2cbc6b8SAndy Fiddaman return;
393*c2cbc6b8SAndy Fiddaman
394*c2cbc6b8SAndy Fiddaman bzero(mp->b_rptr, alloclen);
395*c2cbc6b8SAndy Fiddaman mp->b_wptr += alloclen;
396*c2cbc6b8SAndy Fiddaman end = mp->b_wptr;
397*c2cbc6b8SAndy Fiddaman
398*c2cbc6b8SAndy Fiddaman samsg = (sadb_msg_t *)mp->b_rptr;
399*c2cbc6b8SAndy Fiddaman samsg->sadb_msg_version = PF_KEY_V2;
400*c2cbc6b8SAndy Fiddaman samsg->sadb_msg_type = SADB_EXPIRE;
401*c2cbc6b8SAndy Fiddaman samsg->sadb_msg_errno = 0;
402*c2cbc6b8SAndy Fiddaman samsg->sadb_msg_satype = SADB_X_SATYPE_TCPSIG;
403*c2cbc6b8SAndy Fiddaman samsg->sadb_msg_reserved = 0;
404*c2cbc6b8SAndy Fiddaman samsg->sadb_msg_seq = 0;
405*c2cbc6b8SAndy Fiddaman samsg->sadb_msg_pid = 0;
406*c2cbc6b8SAndy Fiddaman samsg->sadb_msg_len = (uint16_t)SADB_8TO64(alloclen);
407*c2cbc6b8SAndy Fiddaman
408*c2cbc6b8SAndy Fiddaman cur = (uint8_t *)(samsg + 1);
409*c2cbc6b8SAndy Fiddaman cur = tcpsig_make_sa_ext(cur, end, sa);
410*c2cbc6b8SAndy Fiddaman cur = tcpsig_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC,
411*c2cbc6b8SAndy Fiddaman sa->ts_family, &sa->ts_src);
412*c2cbc6b8SAndy Fiddaman cur = tcpsig_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST,
413*c2cbc6b8SAndy Fiddaman sa->ts_family, &sa->ts_dst);
414*c2cbc6b8SAndy Fiddaman
415*c2cbc6b8SAndy Fiddaman if (cur == NULL) {
416*c2cbc6b8SAndy Fiddaman freeb(mp);
417*c2cbc6b8SAndy Fiddaman return;
418*c2cbc6b8SAndy Fiddaman }
419*c2cbc6b8SAndy Fiddaman
420*c2cbc6b8SAndy Fiddaman lt = (sadb_lifetime_t *)cur;
421*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
422*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
423*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_allocations = 0;
424*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_bytes = 0;
425*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_addtime = sa->ts_addtime;
426*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_usetime = sa->ts_usetime;
427*c2cbc6b8SAndy Fiddaman
428*c2cbc6b8SAndy Fiddaman lt++;
429*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
430*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_exttype = ltt;
431*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_allocations = 0;
432*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_bytes = 0;
433*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_addtime = sa->ts_hardaddlt;
434*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_usetime = sa->ts_harduselt;
435*c2cbc6b8SAndy Fiddaman
436*c2cbc6b8SAndy Fiddaman keysock_passup(mp, (sadb_msg_t *)mp->b_rptr,
437*c2cbc6b8SAndy Fiddaman 0, NULL, B_TRUE, ks->keysock_keystack);
438*c2cbc6b8SAndy Fiddaman }
439*c2cbc6b8SAndy Fiddaman
440*c2cbc6b8SAndy Fiddaman static void
tcpsig_sa_age(keysock_t * ks,tcp_stack_t * tcps)441*c2cbc6b8SAndy Fiddaman tcpsig_sa_age(keysock_t *ks, tcp_stack_t *tcps)
442*c2cbc6b8SAndy Fiddaman {
443*c2cbc6b8SAndy Fiddaman tcpsig_db_t *db = tcpsig_db(tcps);
444*c2cbc6b8SAndy Fiddaman tcpsig_sa_t *nextsa;
445*c2cbc6b8SAndy Fiddaman const time_t now = gethrestime_sec();
446*c2cbc6b8SAndy Fiddaman
447*c2cbc6b8SAndy Fiddaman rw_enter(&db->td_lock, RW_WRITER);
448*c2cbc6b8SAndy Fiddaman nextsa = list_head(&db->td_salist);
449*c2cbc6b8SAndy Fiddaman while (nextsa != NULL) {
450*c2cbc6b8SAndy Fiddaman tcpsig_sa_t *sa = nextsa;
451*c2cbc6b8SAndy Fiddaman
452*c2cbc6b8SAndy Fiddaman nextsa = list_next(&db->td_salist, sa);
453*c2cbc6b8SAndy Fiddaman
454*c2cbc6b8SAndy Fiddaman mutex_enter(&sa->ts_lock);
455*c2cbc6b8SAndy Fiddaman
456*c2cbc6b8SAndy Fiddaman if (sa->ts_tombstoned) {
457*c2cbc6b8SAndy Fiddaman mutex_exit(&sa->ts_lock);
458*c2cbc6b8SAndy Fiddaman continue;
459*c2cbc6b8SAndy Fiddaman }
460*c2cbc6b8SAndy Fiddaman
461*c2cbc6b8SAndy Fiddaman if (EXPIRED(sa, hardexpiretime, now)) {
462*c2cbc6b8SAndy Fiddaman sa->ts_state = IPSA_STATE_DEAD;
463*c2cbc6b8SAndy Fiddaman tcpsig_sa_expiremsg(ks, sa, SADB_EXT_LIFETIME_HARD);
464*c2cbc6b8SAndy Fiddaman if (sa->ts_refcnt > 0) {
465*c2cbc6b8SAndy Fiddaman sa->ts_tombstoned = true;
466*c2cbc6b8SAndy Fiddaman mutex_exit(&sa->ts_lock);
467*c2cbc6b8SAndy Fiddaman } else {
468*c2cbc6b8SAndy Fiddaman list_remove(&db->td_salist, sa);
469*c2cbc6b8SAndy Fiddaman mutex_exit(&sa->ts_lock);
470*c2cbc6b8SAndy Fiddaman tcpsig_sa_free(sa);
471*c2cbc6b8SAndy Fiddaman }
472*c2cbc6b8SAndy Fiddaman continue;
473*c2cbc6b8SAndy Fiddaman }
474*c2cbc6b8SAndy Fiddaman
475*c2cbc6b8SAndy Fiddaman if (EXPIRED(sa, softexpiretime, now) &&
476*c2cbc6b8SAndy Fiddaman sa->ts_state == IPSA_STATE_MATURE) {
477*c2cbc6b8SAndy Fiddaman sa->ts_state = IPSA_STATE_DYING;
478*c2cbc6b8SAndy Fiddaman tcpsig_sa_expiremsg(ks, sa, SADB_EXT_LIFETIME_SOFT);
479*c2cbc6b8SAndy Fiddaman }
480*c2cbc6b8SAndy Fiddaman
481*c2cbc6b8SAndy Fiddaman mutex_exit(&sa->ts_lock);
482*c2cbc6b8SAndy Fiddaman }
483*c2cbc6b8SAndy Fiddaman
484*c2cbc6b8SAndy Fiddaman rw_exit(&db->td_lock);
485*c2cbc6b8SAndy Fiddaman }
486*c2cbc6b8SAndy Fiddaman
4871edba515SAndy Fiddaman static void
tcpsig_sa_free(tcpsig_sa_t * sa)4881edba515SAndy Fiddaman tcpsig_sa_free(tcpsig_sa_t *sa)
4891edba515SAndy Fiddaman {
4901edba515SAndy Fiddaman ASSERT0(sa->ts_refcnt);
4911edba515SAndy Fiddaman mutex_destroy(&sa->ts_lock);
4921edba515SAndy Fiddaman kmem_free(sa->ts_key.sak_key, sa->ts_key.sak_keylen);
4931edba515SAndy Fiddaman kmem_free(sa, sizeof (*sa));
4941edba515SAndy Fiddaman }
4951edba515SAndy Fiddaman
4961edba515SAndy Fiddaman void
tcpsig_sa_rele(tcpsig_sa_t * sa)4971edba515SAndy Fiddaman tcpsig_sa_rele(tcpsig_sa_t *sa)
4981edba515SAndy Fiddaman {
4991edba515SAndy Fiddaman mutex_enter(&sa->ts_lock);
5001edba515SAndy Fiddaman VERIFY3U(sa->ts_refcnt, >, 0);
5011edba515SAndy Fiddaman sa->ts_refcnt--;
5021edba515SAndy Fiddaman /*
5031edba515SAndy Fiddaman * If we are tombstoned (have been marked as deleted) and the reference
5041edba515SAndy Fiddaman * count has now dropped to zero, then we can go ahead and finally
5051edba515SAndy Fiddaman * remove this SA from the database.
5061edba515SAndy Fiddaman */
5071edba515SAndy Fiddaman if (sa->ts_tombstoned && sa->ts_refcnt == 0) {
5081edba515SAndy Fiddaman tcpsig_db_t *db = tcpsig_db(sa->ts_stack);
5091edba515SAndy Fiddaman
5101edba515SAndy Fiddaman /*
5111edba515SAndy Fiddaman * To maintain the required lock ordering, we need to drop the
5121edba515SAndy Fiddaman * lock on the SA while acquiring the RW lock on the list. Take
5131edba515SAndy Fiddaman * an additional hold before doing this dance and drop it once
5141edba515SAndy Fiddaman * we have re-gained the lock.
5151edba515SAndy Fiddaman */
5161edba515SAndy Fiddaman sa->ts_refcnt++;
5171edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
5181edba515SAndy Fiddaman rw_enter(&db->td_lock, RW_WRITER);
5191edba515SAndy Fiddaman mutex_enter(&sa->ts_lock);
5201edba515SAndy Fiddaman sa->ts_refcnt--;
5211edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
5221edba515SAndy Fiddaman
5231edba515SAndy Fiddaman list_remove(&db->td_salist, sa);
5241edba515SAndy Fiddaman
5251edba515SAndy Fiddaman rw_exit(&db->td_lock);
5261edba515SAndy Fiddaman tcpsig_sa_free(sa);
5271edba515SAndy Fiddaman } else {
5281edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
5291edba515SAndy Fiddaman }
5301edba515SAndy Fiddaman }
5311edba515SAndy Fiddaman
5321edba515SAndy Fiddaman static bool
tcpsig_sa_match4(tcpsig_sa_t * sa,struct sockaddr_storage * src_s,struct sockaddr_storage * dst_s)5331edba515SAndy Fiddaman tcpsig_sa_match4(tcpsig_sa_t *sa, struct sockaddr_storage *src_s,
5341edba515SAndy Fiddaman struct sockaddr_storage *dst_s)
5351edba515SAndy Fiddaman {
5361edba515SAndy Fiddaman sin_t msrc, mdst, *src, *dst, *sasrc, *sadst;
5371edba515SAndy Fiddaman
5381edba515SAndy Fiddaman if (src_s->ss_family != AF_INET)
5391edba515SAndy Fiddaman return (false);
5401edba515SAndy Fiddaman
5411edba515SAndy Fiddaman src = (sin_t *)src_s;
5421edba515SAndy Fiddaman dst = (sin_t *)dst_s;
5431edba515SAndy Fiddaman
5441edba515SAndy Fiddaman if (sa->ts_family == AF_INET6) {
5451edba515SAndy Fiddaman sin6_t *sasrc6 = (sin6_t *)&sa->ts_src;
5461edba515SAndy Fiddaman sin6_t *sadst6 = (sin6_t *)&sa->ts_dst;
5471edba515SAndy Fiddaman
5481edba515SAndy Fiddaman if (!IN6_IS_ADDR_V4MAPPED(&sasrc6->sin6_addr) ||
5491edba515SAndy Fiddaman !IN6_IS_ADDR_V4MAPPED(&sadst6->sin6_addr)) {
5501edba515SAndy Fiddaman return (false);
5511edba515SAndy Fiddaman }
5521edba515SAndy Fiddaman
5531edba515SAndy Fiddaman msrc = sin_null;
5541edba515SAndy Fiddaman msrc.sin_family = AF_INET;
5551edba515SAndy Fiddaman msrc.sin_port = sasrc6->sin6_port;
5561edba515SAndy Fiddaman IN6_V4MAPPED_TO_INADDR(&sasrc6->sin6_addr, &msrc.sin_addr);
5571edba515SAndy Fiddaman sasrc = &msrc;
5581edba515SAndy Fiddaman
5591edba515SAndy Fiddaman mdst = sin_null;
5601edba515SAndy Fiddaman mdst.sin_family = AF_INET;
5611edba515SAndy Fiddaman mdst.sin_port = sadst6->sin6_port;
5621edba515SAndy Fiddaman IN6_V4MAPPED_TO_INADDR(&sadst6->sin6_addr, &mdst.sin_addr);
5631edba515SAndy Fiddaman sadst = &mdst;
5641edba515SAndy Fiddaman } else {
5651edba515SAndy Fiddaman sasrc = (sin_t *)&sa->ts_src;
5661edba515SAndy Fiddaman sadst = (sin_t *)&sa->ts_dst;
5671edba515SAndy Fiddaman }
5681edba515SAndy Fiddaman
5691edba515SAndy Fiddaman if (sasrc->sin_port != 0 && sasrc->sin_port != src->sin_port)
5701edba515SAndy Fiddaman return (false);
5711edba515SAndy Fiddaman if (sadst->sin_port != 0 && sadst->sin_port != dst->sin_port)
5721edba515SAndy Fiddaman return (false);
5731edba515SAndy Fiddaman
5741edba515SAndy Fiddaman if (sasrc->sin_addr.s_addr != src->sin_addr.s_addr)
5751edba515SAndy Fiddaman return (false);
5761edba515SAndy Fiddaman if (sadst->sin_addr.s_addr != dst->sin_addr.s_addr)
5771edba515SAndy Fiddaman return (false);
5781edba515SAndy Fiddaman
5791edba515SAndy Fiddaman return (true);
5801edba515SAndy Fiddaman }
5811edba515SAndy Fiddaman
5821edba515SAndy Fiddaman static bool
tcpsig_sa_match6(tcpsig_sa_t * sa,struct sockaddr_storage * src_s,struct sockaddr_storage * dst_s)5831edba515SAndy Fiddaman tcpsig_sa_match6(tcpsig_sa_t *sa, struct sockaddr_storage *src_s,
5841edba515SAndy Fiddaman struct sockaddr_storage *dst_s)
5851edba515SAndy Fiddaman {
5861edba515SAndy Fiddaman sin6_t *src, *dst, *sasrc, *sadst;
5871edba515SAndy Fiddaman
5881edba515SAndy Fiddaman if (src_s->ss_family != AF_INET6 || sa->ts_src.ss_family != AF_INET6)
5891edba515SAndy Fiddaman return (false);
5901edba515SAndy Fiddaman
5911edba515SAndy Fiddaman src = (sin6_t *)src_s;
5921edba515SAndy Fiddaman dst = (sin6_t *)dst_s;
5931edba515SAndy Fiddaman
5941edba515SAndy Fiddaman sasrc = (sin6_t *)&sa->ts_src;
5951edba515SAndy Fiddaman sadst = (sin6_t *)&sa->ts_dst;
5961edba515SAndy Fiddaman
5971edba515SAndy Fiddaman if (sasrc->sin6_port != 0 && sasrc->sin6_port != src->sin6_port)
5981edba515SAndy Fiddaman return (false);
5991edba515SAndy Fiddaman if (sadst->sin6_port != 0 && sadst->sin6_port != dst->sin6_port)
6001edba515SAndy Fiddaman return (false);
6011edba515SAndy Fiddaman
6021edba515SAndy Fiddaman if (!IN6_ARE_ADDR_EQUAL(&sasrc->sin6_addr, &src->sin6_addr))
6031edba515SAndy Fiddaman return (false);
6041edba515SAndy Fiddaman if (!IN6_ARE_ADDR_EQUAL(&sadst->sin6_addr, &dst->sin6_addr))
6051edba515SAndy Fiddaman return (false);
6061edba515SAndy Fiddaman
6071edba515SAndy Fiddaman return (true);
6081edba515SAndy Fiddaman }
6091edba515SAndy Fiddaman
6101edba515SAndy Fiddaman static tcpsig_sa_t *
tcpsig_sa_find_held(struct sockaddr_storage * src,struct sockaddr_storage * dst,tcp_stack_t * tcps)6111edba515SAndy Fiddaman tcpsig_sa_find_held(struct sockaddr_storage *src, struct sockaddr_storage *dst,
6121edba515SAndy Fiddaman tcp_stack_t *tcps)
6131edba515SAndy Fiddaman {
6141edba515SAndy Fiddaman tcpsig_db_t *db = tcpsig_db(tcps);
6151edba515SAndy Fiddaman tcpsig_sa_t *sa = NULL;
616*c2cbc6b8SAndy Fiddaman const time_t now = gethrestime_sec();
6171edba515SAndy Fiddaman
6181edba515SAndy Fiddaman ASSERT(RW_LOCK_HELD(&db->td_lock));
6191edba515SAndy Fiddaman
6201edba515SAndy Fiddaman if (src->ss_family != dst->ss_family)
6211edba515SAndy Fiddaman return (NULL);
6221edba515SAndy Fiddaman
6231edba515SAndy Fiddaman for (sa = list_head(&db->td_salist); sa != NULL;
6241edba515SAndy Fiddaman sa = list_next(&db->td_salist, sa)) {
6251edba515SAndy Fiddaman mutex_enter(&sa->ts_lock);
626*c2cbc6b8SAndy Fiddaman /*
627*c2cbc6b8SAndy Fiddaman * We don't consider tombstoned or hard expired entries as a
628*c2cbc6b8SAndy Fiddaman * possible match.
629*c2cbc6b8SAndy Fiddaman */
630*c2cbc6b8SAndy Fiddaman if (sa->ts_tombstoned || EXPIRED(sa, hardexpiretime, now)) {
6311edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
6321edba515SAndy Fiddaman continue;
6331edba515SAndy Fiddaman }
6341edba515SAndy Fiddaman if (tcpsig_sa_match4(sa, src, dst) ||
6351edba515SAndy Fiddaman tcpsig_sa_match6(sa, src, dst)) {
6361edba515SAndy Fiddaman sa->ts_refcnt++;
6371edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
6381edba515SAndy Fiddaman break;
6391edba515SAndy Fiddaman }
6401edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
6411edba515SAndy Fiddaman }
6421edba515SAndy Fiddaman
6431edba515SAndy Fiddaman return (sa);
6441edba515SAndy Fiddaman }
6451edba515SAndy Fiddaman
6461edba515SAndy Fiddaman static tcpsig_sa_t *
tcpsig_sa_find(struct sockaddr_storage * src,struct sockaddr_storage * dst,tcp_stack_t * tcps)6471edba515SAndy Fiddaman tcpsig_sa_find(struct sockaddr_storage *src, struct sockaddr_storage *dst,
6481edba515SAndy Fiddaman tcp_stack_t *tcps)
6491edba515SAndy Fiddaman {
6501edba515SAndy Fiddaman tcpsig_db_t *db = tcpsig_db(tcps);
6511edba515SAndy Fiddaman tcpsig_sa_t *sa;
6521edba515SAndy Fiddaman
6531edba515SAndy Fiddaman rw_enter(&db->td_lock, RW_READER);
6541edba515SAndy Fiddaman sa = tcpsig_sa_find_held(src, dst, tcps);
6551edba515SAndy Fiddaman rw_exit(&db->td_lock);
6561edba515SAndy Fiddaman
6571edba515SAndy Fiddaman return (sa);
6581edba515SAndy Fiddaman }
6591edba515SAndy Fiddaman
6601edba515SAndy Fiddaman static int
tcpsig_sa_flush(keysock_t * ks,tcp_stack_t * tcps,int * diagp)6611edba515SAndy Fiddaman tcpsig_sa_flush(keysock_t *ks, tcp_stack_t *tcps, int *diagp)
6621edba515SAndy Fiddaman {
6631edba515SAndy Fiddaman tcpsig_db_t *db = tcpsig_db(tcps);
6641edba515SAndy Fiddaman tcpsig_sa_t *nextsa;
6651edba515SAndy Fiddaman
6661edba515SAndy Fiddaman rw_enter(&db->td_lock, RW_WRITER);
6671edba515SAndy Fiddaman nextsa = list_head(&db->td_salist);
6681edba515SAndy Fiddaman while (nextsa != NULL) {
6691edba515SAndy Fiddaman tcpsig_sa_t *sa = nextsa;
6701edba515SAndy Fiddaman
6711edba515SAndy Fiddaman nextsa = list_next(&db->td_salist, sa);
6721edba515SAndy Fiddaman
6731edba515SAndy Fiddaman mutex_enter(&sa->ts_lock);
6741edba515SAndy Fiddaman if (sa->ts_refcnt > 0) {
6751edba515SAndy Fiddaman sa->ts_tombstoned = true;
6761edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
6771edba515SAndy Fiddaman continue;
6781edba515SAndy Fiddaman }
6791edba515SAndy Fiddaman
6801edba515SAndy Fiddaman list_remove(&db->td_salist, sa);
6811edba515SAndy Fiddaman
6821edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
6831edba515SAndy Fiddaman tcpsig_sa_free(sa);
6841edba515SAndy Fiddaman }
6851edba515SAndy Fiddaman
6861edba515SAndy Fiddaman rw_exit(&db->td_lock);
6871edba515SAndy Fiddaman
6881edba515SAndy Fiddaman return (0);
6891edba515SAndy Fiddaman }
6901edba515SAndy Fiddaman
6911edba515SAndy Fiddaman static int
tcpsig_sa_add(keysock_t * ks,tcp_stack_t * tcps,keysock_in_t * ksi,sadb_ext_t ** extv,int * diagp)6921edba515SAndy Fiddaman tcpsig_sa_add(keysock_t *ks, tcp_stack_t *tcps, keysock_in_t *ksi,
6931edba515SAndy Fiddaman sadb_ext_t **extv, int *diagp)
6941edba515SAndy Fiddaman {
6951edba515SAndy Fiddaman tcpsig_db_t *db;
6961edba515SAndy Fiddaman sadb_address_t *srcext, *dstext;
697*c2cbc6b8SAndy Fiddaman sadb_lifetime_t *soft, *hard;
6981edba515SAndy Fiddaman sadb_sa_t *assoc;
6991edba515SAndy Fiddaman struct sockaddr_storage *src, *dst;
7001edba515SAndy Fiddaman sadb_key_t *key;
7011edba515SAndy Fiddaman tcpsig_sa_t *sa, *dupsa;
7021edba515SAndy Fiddaman int ret = 0;
7031edba515SAndy Fiddaman
7041edba515SAndy Fiddaman assoc = (sadb_sa_t *)extv[SADB_EXT_SA];
7051edba515SAndy Fiddaman srcext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_SRC];
7061edba515SAndy Fiddaman dstext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_DST];
7071edba515SAndy Fiddaman key = (sadb_key_t *)extv[SADB_X_EXT_STR_AUTH];
708*c2cbc6b8SAndy Fiddaman soft = (sadb_lifetime_t *)extv[SADB_EXT_LIFETIME_SOFT];
709*c2cbc6b8SAndy Fiddaman hard = (sadb_lifetime_t *)extv[SADB_EXT_LIFETIME_HARD];
7101edba515SAndy Fiddaman
7111edba515SAndy Fiddaman if (assoc == NULL) {
7121edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_MISSING_SA;
7131edba515SAndy Fiddaman return (EINVAL);
7141edba515SAndy Fiddaman }
7151edba515SAndy Fiddaman
7161edba515SAndy Fiddaman if (srcext == NULL) {
7171edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_MISSING_SRC;
7181edba515SAndy Fiddaman return (EINVAL);
7191edba515SAndy Fiddaman }
7201edba515SAndy Fiddaman
7211edba515SAndy Fiddaman if (dstext == NULL) {
7221edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_MISSING_DST;
7231edba515SAndy Fiddaman return (EINVAL);
7241edba515SAndy Fiddaman }
7251edba515SAndy Fiddaman
7261edba515SAndy Fiddaman if (key == NULL) {
7271edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_MISSING_ASTR;
7281edba515SAndy Fiddaman return (EINVAL);
7291edba515SAndy Fiddaman }
7301edba515SAndy Fiddaman
731*c2cbc6b8SAndy Fiddaman if ((*diagp = tcpsig_check_lifetimes(hard, soft)) !=
732*c2cbc6b8SAndy Fiddaman SADB_X_DIAGNOSTIC_NONE) {
733*c2cbc6b8SAndy Fiddaman return (EINVAL);
734*c2cbc6b8SAndy Fiddaman }
735*c2cbc6b8SAndy Fiddaman
7361edba515SAndy Fiddaman src = (struct sockaddr_storage *)(srcext + 1);
7371edba515SAndy Fiddaman dst = (struct sockaddr_storage *)(dstext + 1);
7381edba515SAndy Fiddaman
7391edba515SAndy Fiddaman if (src->ss_family != dst->ss_family) {
7401edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_AF_MISMATCH;
7411edba515SAndy Fiddaman return (EINVAL);
7421edba515SAndy Fiddaman }
7431edba515SAndy Fiddaman
7441edba515SAndy Fiddaman if (src->ss_family != AF_INET && src->ss_family != AF_INET6) {
7451edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_BAD_SRC_AF;
7461edba515SAndy Fiddaman return (EINVAL);
7471edba515SAndy Fiddaman }
7481edba515SAndy Fiddaman
7491edba515SAndy Fiddaman /* We only support MD5 */
7501edba515SAndy Fiddaman if (assoc->sadb_sa_auth != SADB_AALG_MD5) {
7511edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_BAD_AALG;
7521edba515SAndy Fiddaman return (EINVAL);
7531edba515SAndy Fiddaman }
7541edba515SAndy Fiddaman
7551edba515SAndy Fiddaman /* The authentication key length must be a multiple of whole bytes */
7561edba515SAndy Fiddaman if ((key->sadb_key_bits & 0x7) != 0) {
7571edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_MALFORMED_AKEY;
7581edba515SAndy Fiddaman return (EINVAL);
7591edba515SAndy Fiddaman }
7601edba515SAndy Fiddaman
7611edba515SAndy Fiddaman db = tcpsig_db(tcps);
7621edba515SAndy Fiddaman
7631edba515SAndy Fiddaman sa = kmem_zalloc(sizeof (*sa), KM_NOSLEEP_LAZY);
7641edba515SAndy Fiddaman if (sa == NULL)
7651edba515SAndy Fiddaman return (ENOMEM);
7661edba515SAndy Fiddaman
7671edba515SAndy Fiddaman sa->ts_stack = tcps;
7681edba515SAndy Fiddaman sa->ts_family = src->ss_family;
7691edba515SAndy Fiddaman if (sa->ts_family == AF_INET6) {
7701edba515SAndy Fiddaman bcopy(src, (sin6_t *)&sa->ts_src, sizeof (sin6_t));
7711edba515SAndy Fiddaman bcopy(dst, (sin6_t *)&sa->ts_dst, sizeof (sin6_t));
7721edba515SAndy Fiddaman } else {
7731edba515SAndy Fiddaman bcopy(src, (sin_t *)&sa->ts_src, sizeof (sin_t));
7741edba515SAndy Fiddaman bcopy(dst, (sin_t *)&sa->ts_dst, sizeof (sin_t));
7751edba515SAndy Fiddaman }
7761edba515SAndy Fiddaman
7771edba515SAndy Fiddaman sa->ts_key.sak_algid = assoc->sadb_sa_auth;
7781edba515SAndy Fiddaman sa->ts_key.sak_keylen = SADB_1TO8(key->sadb_key_bits);
7791edba515SAndy Fiddaman sa->ts_key.sak_keybits = key->sadb_key_bits;
7801edba515SAndy Fiddaman
7811edba515SAndy Fiddaman sa->ts_key.sak_key = kmem_alloc(sa->ts_key.sak_keylen,
7821edba515SAndy Fiddaman KM_NOSLEEP_LAZY);
7831edba515SAndy Fiddaman if (sa->ts_key.sak_key == NULL) {
7841edba515SAndy Fiddaman kmem_free(sa, sizeof (*sa));
7851edba515SAndy Fiddaman return (ENOMEM);
7861edba515SAndy Fiddaman }
7871edba515SAndy Fiddaman bcopy(key + 1, sa->ts_key.sak_key, sa->ts_key.sak_keylen);
7881edba515SAndy Fiddaman bzero(key + 1, sa->ts_key.sak_keylen);
7891edba515SAndy Fiddaman
7901edba515SAndy Fiddaman mutex_init(&sa->ts_lock, NULL, MUTEX_DEFAULT, NULL);
791*c2cbc6b8SAndy Fiddaman
792*c2cbc6b8SAndy Fiddaman sa->ts_state = SADB_SASTATE_MATURE;
793*c2cbc6b8SAndy Fiddaman sa->ts_addtime = gethrestime_sec();
794*c2cbc6b8SAndy Fiddaman sa->ts_usetime = 0;
795*c2cbc6b8SAndy Fiddaman if (soft != NULL) {
796*c2cbc6b8SAndy Fiddaman sa->ts_softaddlt = soft->sadb_lifetime_addtime;
797*c2cbc6b8SAndy Fiddaman sa->ts_softuselt = soft->sadb_lifetime_usetime;
798*c2cbc6b8SAndy Fiddaman SET_EXPIRE(sa, softaddlt, softexpiretime);
799*c2cbc6b8SAndy Fiddaman }
800*c2cbc6b8SAndy Fiddaman
801*c2cbc6b8SAndy Fiddaman if (hard != NULL) {
802*c2cbc6b8SAndy Fiddaman sa->ts_hardaddlt = hard->sadb_lifetime_addtime;
803*c2cbc6b8SAndy Fiddaman sa->ts_harduselt = hard->sadb_lifetime_usetime;
804*c2cbc6b8SAndy Fiddaman SET_EXPIRE(sa, hardaddlt, hardexpiretime);
805*c2cbc6b8SAndy Fiddaman }
806*c2cbc6b8SAndy Fiddaman
8071edba515SAndy Fiddaman sa->ts_refcnt = 0;
8081edba515SAndy Fiddaman sa->ts_tombstoned = false;
8091edba515SAndy Fiddaman
8101edba515SAndy Fiddaman rw_enter(&db->td_lock, RW_WRITER);
8111edba515SAndy Fiddaman if ((dupsa = tcpsig_sa_find_held(src, dst, tcps)) != NULL) {
8121edba515SAndy Fiddaman rw_exit(&db->td_lock);
8131edba515SAndy Fiddaman tcpsig_sa_rele(dupsa);
8141edba515SAndy Fiddaman tcpsig_sa_free(sa);
8151edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_DUPLICATE_SA;
8161edba515SAndy Fiddaman ret = EEXIST;
8171edba515SAndy Fiddaman } else {
8181edba515SAndy Fiddaman list_insert_tail(&db->td_salist, sa);
8191edba515SAndy Fiddaman rw_exit(&db->td_lock);
8201edba515SAndy Fiddaman }
8211edba515SAndy Fiddaman
8221edba515SAndy Fiddaman return (ret);
8231edba515SAndy Fiddaman }
8241edba515SAndy Fiddaman
825*c2cbc6b8SAndy Fiddaman /*
826*c2cbc6b8SAndy Fiddaman * Handle an UPDATE message. We only support updating lifetimes.
827*c2cbc6b8SAndy Fiddaman */
828*c2cbc6b8SAndy Fiddaman static int
tcpsig_sa_update(keysock_t * ks,tcp_stack_t * tcps,keysock_in_t * ksi,sadb_ext_t ** extv,int * diagp)829*c2cbc6b8SAndy Fiddaman tcpsig_sa_update(keysock_t *ks, tcp_stack_t *tcps, keysock_in_t *ksi,
830*c2cbc6b8SAndy Fiddaman sadb_ext_t **extv, int *diagp)
8311edba515SAndy Fiddaman {
832*c2cbc6b8SAndy Fiddaman tcpsig_db_t *db;
833*c2cbc6b8SAndy Fiddaman sadb_address_t *srcext, *dstext;
834*c2cbc6b8SAndy Fiddaman sadb_lifetime_t *soft, *hard;
835*c2cbc6b8SAndy Fiddaman struct sockaddr_storage *src, *dst;
836*c2cbc6b8SAndy Fiddaman tcpsig_sa_t *sa;
8371edba515SAndy Fiddaman
838*c2cbc6b8SAndy Fiddaman srcext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_SRC];
839*c2cbc6b8SAndy Fiddaman dstext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_DST];
840*c2cbc6b8SAndy Fiddaman soft = (sadb_lifetime_t *)extv[SADB_EXT_LIFETIME_SOFT];
841*c2cbc6b8SAndy Fiddaman hard = (sadb_lifetime_t *)extv[SADB_EXT_LIFETIME_HARD];
8421edba515SAndy Fiddaman
843*c2cbc6b8SAndy Fiddaman if (srcext == NULL) {
844*c2cbc6b8SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_MISSING_SRC;
845*c2cbc6b8SAndy Fiddaman return (EINVAL);
8461edba515SAndy Fiddaman }
8471edba515SAndy Fiddaman
848*c2cbc6b8SAndy Fiddaman if (dstext == NULL) {
849*c2cbc6b8SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_MISSING_DST;
850*c2cbc6b8SAndy Fiddaman return (EINVAL);
851*c2cbc6b8SAndy Fiddaman }
8521edba515SAndy Fiddaman
8531edba515SAndy Fiddaman
854*c2cbc6b8SAndy Fiddaman if ((*diagp = tcpsig_check_lifetimes(hard, soft)) !=
855*c2cbc6b8SAndy Fiddaman SADB_X_DIAGNOSTIC_NONE) {
856*c2cbc6b8SAndy Fiddaman return (EINVAL);
857*c2cbc6b8SAndy Fiddaman }
858*c2cbc6b8SAndy Fiddaman
859*c2cbc6b8SAndy Fiddaman src = (struct sockaddr_storage *)(srcext + 1);
860*c2cbc6b8SAndy Fiddaman dst = (struct sockaddr_storage *)(dstext + 1);
861*c2cbc6b8SAndy Fiddaman
862*c2cbc6b8SAndy Fiddaman sa = tcpsig_sa_find(src, dst, tcps);
863*c2cbc6b8SAndy Fiddaman
864*c2cbc6b8SAndy Fiddaman if (sa == NULL) {
865*c2cbc6b8SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_PAIR_SA_NOTFOUND;
866*c2cbc6b8SAndy Fiddaman return (ESRCH);
867*c2cbc6b8SAndy Fiddaman }
868*c2cbc6b8SAndy Fiddaman
869*c2cbc6b8SAndy Fiddaman tcpsig_update_lifetimes(sa, hard, soft);
870*c2cbc6b8SAndy Fiddaman tcpsig_sa_rele(sa);
871*c2cbc6b8SAndy Fiddaman
872*c2cbc6b8SAndy Fiddaman /*
873*c2cbc6b8SAndy Fiddaman * Run an aging pass in case updating the SA lifetimes has resulted in
874*c2cbc6b8SAndy Fiddaman * the SA now being aged out.
875*c2cbc6b8SAndy Fiddaman */
876*c2cbc6b8SAndy Fiddaman tcpsig_sa_age(ks, tcps);
877*c2cbc6b8SAndy Fiddaman
878*c2cbc6b8SAndy Fiddaman return (0);
8791edba515SAndy Fiddaman }
8801edba515SAndy Fiddaman
8811edba515SAndy Fiddaman static mblk_t *
tcpsig_dump_one(const tcpsig_sa_t * sa,sadb_msg_t * samsg)882*c2cbc6b8SAndy Fiddaman tcpsig_dump_one(const tcpsig_sa_t *sa, sadb_msg_t *samsg)
8831edba515SAndy Fiddaman {
884*c2cbc6b8SAndy Fiddaman size_t alloclen, keysize;
8851edba515SAndy Fiddaman sadb_sa_t *assoc;
8861edba515SAndy Fiddaman sadb_msg_t *newsamsg;
8871edba515SAndy Fiddaman uint8_t *cur, *end;
8881edba515SAndy Fiddaman sadb_key_t *key;
8891edba515SAndy Fiddaman mblk_t *mp;
890*c2cbc6b8SAndy Fiddaman bool soft = false, hard = false;
8911edba515SAndy Fiddaman
892*c2cbc6b8SAndy Fiddaman ASSERT(MUTEX_HELD(&sa->ts_lock));
8931edba515SAndy Fiddaman
894*c2cbc6b8SAndy Fiddaman alloclen = sizeof (sadb_msg_t) + sizeof (sadb_sa_t) +
895*c2cbc6b8SAndy Fiddaman 2 * tcpsig_addr_extsize(sa);
896*c2cbc6b8SAndy Fiddaman
897*c2cbc6b8SAndy Fiddaman if (sa->ts_softaddlt != 0 || sa->ts_softuselt != 0) {
898*c2cbc6b8SAndy Fiddaman alloclen += sizeof (sadb_lifetime_t);
899*c2cbc6b8SAndy Fiddaman soft = true;
9001edba515SAndy Fiddaman }
901*c2cbc6b8SAndy Fiddaman
902*c2cbc6b8SAndy Fiddaman if (sa->ts_hardaddlt != 0 || sa->ts_harduselt != 0) {
903*c2cbc6b8SAndy Fiddaman alloclen += sizeof (sadb_lifetime_t);
904*c2cbc6b8SAndy Fiddaman hard = true;
905*c2cbc6b8SAndy Fiddaman }
906*c2cbc6b8SAndy Fiddaman
907*c2cbc6b8SAndy Fiddaman /* Add space for LIFETIME_CURRENT */
908*c2cbc6b8SAndy Fiddaman if (soft || hard)
909*c2cbc6b8SAndy Fiddaman alloclen += sizeof (sadb_lifetime_t);
910*c2cbc6b8SAndy Fiddaman
9111edba515SAndy Fiddaman keysize = roundup(sizeof (sadb_key_t) + sa->ts_key.sak_keylen,
9121edba515SAndy Fiddaman sizeof (uint64_t));
9131edba515SAndy Fiddaman
914*c2cbc6b8SAndy Fiddaman alloclen += keysize;
9151edba515SAndy Fiddaman
9161edba515SAndy Fiddaman mp = allocb(alloclen, BPRI_HI);
9171edba515SAndy Fiddaman if (mp == NULL)
9181edba515SAndy Fiddaman return (NULL);
9191edba515SAndy Fiddaman
9201edba515SAndy Fiddaman bzero(mp->b_rptr, alloclen);
9211edba515SAndy Fiddaman mp->b_wptr += alloclen;
9221edba515SAndy Fiddaman end = mp->b_wptr;
9231edba515SAndy Fiddaman
9241edba515SAndy Fiddaman newsamsg = (sadb_msg_t *)mp->b_rptr;
9251edba515SAndy Fiddaman *newsamsg = *samsg;
9261edba515SAndy Fiddaman newsamsg->sadb_msg_len = (uint16_t)SADB_8TO64(alloclen);
9271edba515SAndy Fiddaman
928*c2cbc6b8SAndy Fiddaman cur = (uint8_t *)(newsamsg + 1);
929*c2cbc6b8SAndy Fiddaman cur = tcpsig_make_sa_ext(cur, end, sa);
9301edba515SAndy Fiddaman cur = tcpsig_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC,
9311edba515SAndy Fiddaman sa->ts_family, &sa->ts_src);
9321edba515SAndy Fiddaman cur = tcpsig_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST,
9331edba515SAndy Fiddaman sa->ts_family, &sa->ts_dst);
9341edba515SAndy Fiddaman
935*c2cbc6b8SAndy Fiddaman if (cur == NULL) {
936*c2cbc6b8SAndy Fiddaman freeb(mp);
9371edba515SAndy Fiddaman return (NULL);
938*c2cbc6b8SAndy Fiddaman }
939*c2cbc6b8SAndy Fiddaman
940*c2cbc6b8SAndy Fiddaman if (soft || hard) {
941*c2cbc6b8SAndy Fiddaman sadb_lifetime_t *lt = (sadb_lifetime_t *)cur;
942*c2cbc6b8SAndy Fiddaman
943*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
944*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
945*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_allocations = 0;
946*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_bytes = 0;
947*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_addtime = sa->ts_addtime;
948*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_usetime = sa->ts_usetime;
949*c2cbc6b8SAndy Fiddaman lt++;
950*c2cbc6b8SAndy Fiddaman
951*c2cbc6b8SAndy Fiddaman if (soft) {
952*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
953*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
954*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_allocations = 0;
955*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_bytes = 0;
956*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_addtime = sa->ts_softaddlt;
957*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_usetime = sa->ts_softuselt;
958*c2cbc6b8SAndy Fiddaman lt++;
959*c2cbc6b8SAndy Fiddaman }
960*c2cbc6b8SAndy Fiddaman if (hard) {
961*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
962*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
963*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_allocations = 0;
964*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_bytes = 0;
965*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_addtime = sa->ts_hardaddlt;
966*c2cbc6b8SAndy Fiddaman lt->sadb_lifetime_usetime = sa->ts_harduselt;
967*c2cbc6b8SAndy Fiddaman lt++;
968*c2cbc6b8SAndy Fiddaman }
969*c2cbc6b8SAndy Fiddaman
970*c2cbc6b8SAndy Fiddaman cur = (uint8_t *)lt;
971*c2cbc6b8SAndy Fiddaman }
9721edba515SAndy Fiddaman
9731edba515SAndy Fiddaman key = (sadb_key_t *)cur;
9741edba515SAndy Fiddaman key->sadb_key_exttype = SADB_X_EXT_STR_AUTH;
9751edba515SAndy Fiddaman key->sadb_key_len = SADB_8TO64(keysize);
9761edba515SAndy Fiddaman key->sadb_key_bits = sa->ts_key.sak_keybits;
9771edba515SAndy Fiddaman key->sadb_key_reserved = 0;
9781edba515SAndy Fiddaman bcopy(sa->ts_key.sak_key, (uint8_t *)(key + 1), sa->ts_key.sak_keylen);
9791edba515SAndy Fiddaman
9801edba515SAndy Fiddaman return (mp);
9811edba515SAndy Fiddaman }
9821edba515SAndy Fiddaman
9831edba515SAndy Fiddaman static int
tcpsig_sa_dump(keysock_t * ks,tcp_stack_t * tcps,sadb_msg_t * samsg,int * diag)9841edba515SAndy Fiddaman tcpsig_sa_dump(keysock_t *ks, tcp_stack_t *tcps, sadb_msg_t *samsg, int *diag)
9851edba515SAndy Fiddaman {
9861edba515SAndy Fiddaman tcpsig_db_t *db;
9871edba515SAndy Fiddaman tcpsig_sa_t *sa;
9881edba515SAndy Fiddaman
9891edba515SAndy Fiddaman db = tcpsig_db(tcps);
9901edba515SAndy Fiddaman rw_enter(&db->td_lock, RW_READER);
9911edba515SAndy Fiddaman
9921edba515SAndy Fiddaman for (sa = list_head(&db->td_salist); sa != NULL;
9931edba515SAndy Fiddaman sa = list_next(&db->td_salist, sa)) {
9941edba515SAndy Fiddaman mblk_t *mp;
9951edba515SAndy Fiddaman
9961edba515SAndy Fiddaman mutex_enter(&sa->ts_lock);
9971edba515SAndy Fiddaman if (sa->ts_tombstoned) {
9981edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
9991edba515SAndy Fiddaman continue;
10001edba515SAndy Fiddaman }
1001*c2cbc6b8SAndy Fiddaman mp = tcpsig_dump_one(sa, samsg);
10021edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
10031edba515SAndy Fiddaman
10041edba515SAndy Fiddaman if (mp == NULL) {
10051edba515SAndy Fiddaman rw_exit(&db->td_lock);
10061edba515SAndy Fiddaman return (ENOMEM);
10071edba515SAndy Fiddaman }
10081edba515SAndy Fiddaman keysock_passup(mp, (sadb_msg_t *)mp->b_rptr,
10091edba515SAndy Fiddaman ks->keysock_serial, NULL, B_TRUE, ks->keysock_keystack);
10101edba515SAndy Fiddaman }
10111edba515SAndy Fiddaman
10121edba515SAndy Fiddaman rw_exit(&db->td_lock);
10131edba515SAndy Fiddaman
10141edba515SAndy Fiddaman /* A sequence number of 0 indicates the end of the list */
10151edba515SAndy Fiddaman samsg->sadb_msg_seq = 0;
10161edba515SAndy Fiddaman
10171edba515SAndy Fiddaman return (0);
10181edba515SAndy Fiddaman }
10191edba515SAndy Fiddaman
10201edba515SAndy Fiddaman static int
tcpsig_sa_delget(keysock_t * ks,tcp_stack_t * tcps,sadb_msg_t * samsg,sadb_ext_t ** extv,int * diagp)10211edba515SAndy Fiddaman tcpsig_sa_delget(keysock_t *ks, tcp_stack_t *tcps, sadb_msg_t *samsg,
10221edba515SAndy Fiddaman sadb_ext_t **extv, int *diagp)
10231edba515SAndy Fiddaman {
10241edba515SAndy Fiddaman sadb_address_t *srcext, *dstext;
10251edba515SAndy Fiddaman struct sockaddr_storage *src, *dst;
10261edba515SAndy Fiddaman tcpsig_sa_t *sa;
10271edba515SAndy Fiddaman mblk_t *mp;
10281edba515SAndy Fiddaman
10291edba515SAndy Fiddaman srcext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_SRC];
10301edba515SAndy Fiddaman dstext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_DST];
10311edba515SAndy Fiddaman
10321edba515SAndy Fiddaman if (srcext == NULL) {
10331edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_MISSING_SRC;
10341edba515SAndy Fiddaman return (EINVAL);
10351edba515SAndy Fiddaman }
10361edba515SAndy Fiddaman
10371edba515SAndy Fiddaman if (dstext == NULL) {
10381edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_MISSING_DST;
10391edba515SAndy Fiddaman return (EINVAL);
10401edba515SAndy Fiddaman }
10411edba515SAndy Fiddaman
10421edba515SAndy Fiddaman src = (struct sockaddr_storage *)(srcext + 1);
10431edba515SAndy Fiddaman dst = (struct sockaddr_storage *)(dstext + 1);
10441edba515SAndy Fiddaman
10451edba515SAndy Fiddaman sa = tcpsig_sa_find(src, dst, tcps);
10461edba515SAndy Fiddaman
10471edba515SAndy Fiddaman if (sa == NULL) {
10481edba515SAndy Fiddaman *diagp = SADB_X_DIAGNOSTIC_PAIR_SA_NOTFOUND;
10491edba515SAndy Fiddaman return (ESRCH);
10501edba515SAndy Fiddaman }
10511edba515SAndy Fiddaman
10521edba515SAndy Fiddaman if (samsg->sadb_msg_type == SADB_GET) {
1053*c2cbc6b8SAndy Fiddaman mutex_enter(&sa->ts_lock);
10541edba515SAndy Fiddaman mp = tcpsig_dump_one(sa, samsg);
1055*c2cbc6b8SAndy Fiddaman mutex_exit(&sa->ts_lock);
10561edba515SAndy Fiddaman
1057*c2cbc6b8SAndy Fiddaman if (mp == NULL) {
1058*c2cbc6b8SAndy Fiddaman tcpsig_sa_rele(sa);
10591edba515SAndy Fiddaman return (ENOMEM);
1060*c2cbc6b8SAndy Fiddaman }
10611edba515SAndy Fiddaman keysock_passup(mp, (sadb_msg_t *)mp->b_rptr,
10621edba515SAndy Fiddaman ks->keysock_serial, NULL, B_TRUE, ks->keysock_keystack);
10631edba515SAndy Fiddaman tcpsig_sa_rele(sa);
10641edba515SAndy Fiddaman
10651edba515SAndy Fiddaman return (0);
10661edba515SAndy Fiddaman }
10671edba515SAndy Fiddaman
10683887472aSAndy Fiddaman /*
10693887472aSAndy Fiddaman * Delete the entry.
10703887472aSAndy Fiddaman * At this point we still have a hold on the entry from the find call
10713887472aSAndy Fiddaman * above, so mark it as tombstoned and then release the hold. If
10723887472aSAndy Fiddaman * that causes the reference count to become 0, the entry will be
10733887472aSAndy Fiddaman * removed from the database.
10743887472aSAndy Fiddaman */
10751edba515SAndy Fiddaman
10761edba515SAndy Fiddaman mutex_enter(&sa->ts_lock);
10771edba515SAndy Fiddaman sa->ts_tombstoned = true;
10781edba515SAndy Fiddaman mutex_exit(&sa->ts_lock);
10793887472aSAndy Fiddaman tcpsig_sa_rele(sa);
10801edba515SAndy Fiddaman
10811edba515SAndy Fiddaman return (0);
10821edba515SAndy Fiddaman }
10831edba515SAndy Fiddaman
10841edba515SAndy Fiddaman void
tcpsig_sa_handler(keysock_t * ks,mblk_t * mp,sadb_msg_t * samsg,sadb_ext_t ** extv)10851edba515SAndy Fiddaman tcpsig_sa_handler(keysock_t *ks, mblk_t *mp, sadb_msg_t *samsg,
10861edba515SAndy Fiddaman sadb_ext_t **extv)
10871edba515SAndy Fiddaman {
10881edba515SAndy Fiddaman keysock_stack_t *keystack = ks->keysock_keystack;
10891edba515SAndy Fiddaman netstack_t *nst = keystack->keystack_netstack;
10901edba515SAndy Fiddaman tcp_stack_t *tcps = nst->netstack_tcp;
10911edba515SAndy Fiddaman keysock_in_t *ksi = (keysock_in_t *)mp->b_rptr;
10921edba515SAndy Fiddaman int diag = SADB_X_DIAGNOSTIC_NONE;
10931edba515SAndy Fiddaman int error;
10941edba515SAndy Fiddaman
1095*c2cbc6b8SAndy Fiddaman tcpsig_sa_age(ks, tcps);
1096*c2cbc6b8SAndy Fiddaman
10971edba515SAndy Fiddaman switch (samsg->sadb_msg_type) {
10981edba515SAndy Fiddaman case SADB_ADD:
10991edba515SAndy Fiddaman error = tcpsig_sa_add(ks, tcps, ksi, extv, &diag);
11001edba515SAndy Fiddaman keysock_error(ks, mp, error, diag);
11011edba515SAndy Fiddaman break;
1102*c2cbc6b8SAndy Fiddaman case SADB_UPDATE:
1103*c2cbc6b8SAndy Fiddaman error = tcpsig_sa_update(ks, tcps, ksi, extv, &diag);
1104*c2cbc6b8SAndy Fiddaman keysock_error(ks, mp, error, diag);
1105*c2cbc6b8SAndy Fiddaman break;
11061edba515SAndy Fiddaman case SADB_GET:
11071edba515SAndy Fiddaman case SADB_DELETE:
11081edba515SAndy Fiddaman error = tcpsig_sa_delget(ks, tcps, samsg, extv, &diag);
11091edba515SAndy Fiddaman keysock_error(ks, mp, error, diag);
11101edba515SAndy Fiddaman break;
11111edba515SAndy Fiddaman case SADB_FLUSH:
11121edba515SAndy Fiddaman error = tcpsig_sa_flush(ks, tcps, &diag);
11131edba515SAndy Fiddaman keysock_error(ks, mp, error, diag);
11141edba515SAndy Fiddaman break;
11151edba515SAndy Fiddaman case SADB_DUMP:
11161edba515SAndy Fiddaman error = tcpsig_sa_dump(ks, tcps, samsg, &diag);
11171edba515SAndy Fiddaman keysock_error(ks, mp, error, diag);
11181edba515SAndy Fiddaman break;
11191edba515SAndy Fiddaman default:
11201edba515SAndy Fiddaman keysock_error(ks, mp, EOPNOTSUPP, diag);
11211edba515SAndy Fiddaman break;
11221edba515SAndy Fiddaman }
11231edba515SAndy Fiddaman }
11241edba515SAndy Fiddaman
112554819d46SAndy Fiddaman bool
tcpsig_sa_exists(tcp_t * tcp,bool inbound,tcpsig_sa_t ** sap)112654819d46SAndy Fiddaman tcpsig_sa_exists(tcp_t *tcp, bool inbound, tcpsig_sa_t **sap)
112754819d46SAndy Fiddaman {
112854819d46SAndy Fiddaman tcp_stack_t *tcps = tcp->tcp_tcps;
112954819d46SAndy Fiddaman conn_t *connp = tcp->tcp_connp;
113054819d46SAndy Fiddaman struct sockaddr_storage src, dst;
113154819d46SAndy Fiddaman tcpsig_sa_t *sa;
113254819d46SAndy Fiddaman
113354819d46SAndy Fiddaman bzero(&src, sizeof (src));
113454819d46SAndy Fiddaman bzero(&dst, sizeof (dst));
113554819d46SAndy Fiddaman
113654819d46SAndy Fiddaman if (connp->conn_ipversion == IPV6_VERSION) {
113754819d46SAndy Fiddaman sin6_t *sin6;
113854819d46SAndy Fiddaman
113954819d46SAndy Fiddaman sin6 = (sin6_t *)&src;
114054819d46SAndy Fiddaman sin6->sin6_family = AF_INET6;
114154819d46SAndy Fiddaman if (inbound) {
114254819d46SAndy Fiddaman sin6->sin6_addr = connp->conn_faddr_v6;
114354819d46SAndy Fiddaman sin6->sin6_port = connp->conn_fport;
114454819d46SAndy Fiddaman } else {
114554819d46SAndy Fiddaman sin6->sin6_addr = connp->conn_saddr_v6;
114654819d46SAndy Fiddaman sin6->sin6_port = connp->conn_lport;
114754819d46SAndy Fiddaman }
114854819d46SAndy Fiddaman
114954819d46SAndy Fiddaman sin6 = (sin6_t *)&dst;
115054819d46SAndy Fiddaman sin6->sin6_family = AF_INET6;
115154819d46SAndy Fiddaman if (inbound) {
115254819d46SAndy Fiddaman sin6->sin6_addr = connp->conn_saddr_v6;
115354819d46SAndy Fiddaman sin6->sin6_port = connp->conn_lport;
115454819d46SAndy Fiddaman } else {
115554819d46SAndy Fiddaman sin6->sin6_addr = connp->conn_faddr_v6;
115654819d46SAndy Fiddaman sin6->sin6_port = connp->conn_fport;
115754819d46SAndy Fiddaman }
115854819d46SAndy Fiddaman } else {
115954819d46SAndy Fiddaman sin_t *sin;
116054819d46SAndy Fiddaman
116154819d46SAndy Fiddaman sin = (sin_t *)&src;
116254819d46SAndy Fiddaman sin->sin_family = AF_INET;
116354819d46SAndy Fiddaman if (inbound) {
116454819d46SAndy Fiddaman sin->sin_addr.s_addr = connp->conn_faddr_v4;
116554819d46SAndy Fiddaman sin->sin_port = connp->conn_fport;
116654819d46SAndy Fiddaman } else {
116754819d46SAndy Fiddaman sin->sin_addr.s_addr = connp->conn_saddr_v4;
116854819d46SAndy Fiddaman sin->sin_port = connp->conn_lport;
116954819d46SAndy Fiddaman }
117054819d46SAndy Fiddaman
117154819d46SAndy Fiddaman sin = (sin_t *)&dst;
117254819d46SAndy Fiddaman sin->sin_family = AF_INET;
117354819d46SAndy Fiddaman if (inbound) {
117454819d46SAndy Fiddaman sin->sin_addr.s_addr = connp->conn_saddr_v4;
117554819d46SAndy Fiddaman sin->sin_port = connp->conn_lport;
117654819d46SAndy Fiddaman } else {
117754819d46SAndy Fiddaman sin->sin_addr.s_addr = connp->conn_faddr_v4;
117854819d46SAndy Fiddaman sin->sin_port = connp->conn_fport;
117954819d46SAndy Fiddaman }
118054819d46SAndy Fiddaman }
118154819d46SAndy Fiddaman
118254819d46SAndy Fiddaman sa = tcpsig_sa_find(&src, &dst, tcps);
118354819d46SAndy Fiddaman
118454819d46SAndy Fiddaman if (sa == NULL)
118554819d46SAndy Fiddaman return (false);
118654819d46SAndy Fiddaman
118754819d46SAndy Fiddaman if (sap != NULL)
118854819d46SAndy Fiddaman *sap = sa;
118954819d46SAndy Fiddaman else
119054819d46SAndy Fiddaman tcpsig_sa_rele(sa);
119154819d46SAndy Fiddaman
119254819d46SAndy Fiddaman return (true);
119354819d46SAndy Fiddaman }
119454819d46SAndy Fiddaman
11951edba515SAndy Fiddaman static void
tcpsig_pseudo_compute4(tcp_t * tcp,int tcplen,MD5_CTX * ctx,bool inbound)11961edba515SAndy Fiddaman tcpsig_pseudo_compute4(tcp_t *tcp, int tcplen, MD5_CTX *ctx, bool inbound)
11971edba515SAndy Fiddaman {
11981edba515SAndy Fiddaman struct ip_pseudo {
11991edba515SAndy Fiddaman struct in_addr ipp_src;
12001edba515SAndy Fiddaman struct in_addr ipp_dst;
12011edba515SAndy Fiddaman uint8_t ipp_pad;
12021edba515SAndy Fiddaman uint8_t ipp_proto;
12031edba515SAndy Fiddaman uint16_t ipp_len;
12041edba515SAndy Fiddaman } ipp;
12051edba515SAndy Fiddaman conn_t *connp = tcp->tcp_connp;
12061edba515SAndy Fiddaman
12071edba515SAndy Fiddaman if (inbound) {
12081edba515SAndy Fiddaman ipp.ipp_src.s_addr = connp->conn_faddr_v4;
12091edba515SAndy Fiddaman ipp.ipp_dst.s_addr = connp->conn_saddr_v4;
12101edba515SAndy Fiddaman } else {
12111edba515SAndy Fiddaman ipp.ipp_src.s_addr = connp->conn_saddr_v4;
12121edba515SAndy Fiddaman ipp.ipp_dst.s_addr = connp->conn_faddr_v4;
12131edba515SAndy Fiddaman }
12141edba515SAndy Fiddaman ipp.ipp_pad = 0;
12151edba515SAndy Fiddaman ipp.ipp_proto = IPPROTO_TCP;
12161edba515SAndy Fiddaman ipp.ipp_len = htons(tcplen);
12171edba515SAndy Fiddaman
12181edba515SAndy Fiddaman DTRACE_PROBE1(ipp4, struct ip_pseudo *, &ipp);
12191edba515SAndy Fiddaman
12201edba515SAndy Fiddaman MD5Update(ctx, (char *)&ipp, sizeof (ipp));
12211edba515SAndy Fiddaman }
12221edba515SAndy Fiddaman
12231edba515SAndy Fiddaman static void
tcpsig_pseudo_compute6(tcp_t * tcp,int tcplen,MD5_CTX * ctx,bool inbound)12241edba515SAndy Fiddaman tcpsig_pseudo_compute6(tcp_t *tcp, int tcplen, MD5_CTX *ctx, bool inbound)
12251edba515SAndy Fiddaman {
12261edba515SAndy Fiddaman struct ip6_pseudo {
12271edba515SAndy Fiddaman struct in6_addr ipp_src;
12281edba515SAndy Fiddaman struct in6_addr ipp_dst;
12291edba515SAndy Fiddaman uint32_t ipp_len;
12301edba515SAndy Fiddaman uint32_t ipp_nxt;
12311edba515SAndy Fiddaman } ip6p;
12321edba515SAndy Fiddaman conn_t *connp = tcp->tcp_connp;
12331edba515SAndy Fiddaman
12341edba515SAndy Fiddaman if (inbound) {
12351edba515SAndy Fiddaman ip6p.ipp_src = connp->conn_faddr_v6;
12361edba515SAndy Fiddaman ip6p.ipp_dst = connp->conn_saddr_v6;
12371edba515SAndy Fiddaman } else {
12381edba515SAndy Fiddaman ip6p.ipp_src = connp->conn_saddr_v6;
12391edba515SAndy Fiddaman ip6p.ipp_dst = connp->conn_faddr_v6;
12401edba515SAndy Fiddaman }
12411edba515SAndy Fiddaman ip6p.ipp_len = htonl(tcplen);
12421edba515SAndy Fiddaman ip6p.ipp_nxt = htonl(IPPROTO_TCP);
12431edba515SAndy Fiddaman
12441edba515SAndy Fiddaman DTRACE_PROBE1(ipp6, struct ip6_pseudo *, &ip6p);
12451edba515SAndy Fiddaman
12461edba515SAndy Fiddaman MD5Update(ctx, (char *)&ip6p, sizeof (ip6p));
12471edba515SAndy Fiddaman }
12481edba515SAndy Fiddaman
12491edba515SAndy Fiddaman bool
tcpsig_signature(mblk_t * mp,tcp_t * tcp,tcpha_t * tcpha,int tcplen,uint8_t * digest,bool inbound)12501edba515SAndy Fiddaman tcpsig_signature(mblk_t *mp, tcp_t *tcp, tcpha_t *tcpha, int tcplen,
12511edba515SAndy Fiddaman uint8_t *digest, bool inbound)
12521edba515SAndy Fiddaman {
12531edba515SAndy Fiddaman tcp_stack_t *tcps = tcp->tcp_tcps;
12541edba515SAndy Fiddaman conn_t *connp = tcp->tcp_connp;
12551edba515SAndy Fiddaman tcpsig_sa_t *sa;
12561edba515SAndy Fiddaman MD5_CTX context;
12571edba515SAndy Fiddaman
12581edba515SAndy Fiddaman /*
12591edba515SAndy Fiddaman * The TCP_MD5SIG option is 20 bytes, including padding, which adds 5
12601edba515SAndy Fiddaman * 32-bit words to the header's 4-bit field. Check that it can fit in
12611edba515SAndy Fiddaman * the current packet.
12621edba515SAndy Fiddaman */
12631edba515SAndy Fiddaman if (!inbound && (tcpha->tha_offset_and_reserved >> 4) > 10) {
12641edba515SAndy Fiddaman TCP_STAT(tcps, tcp_sig_no_space);
12651edba515SAndy Fiddaman return (false);
12661edba515SAndy Fiddaman }
12671edba515SAndy Fiddaman
12681edba515SAndy Fiddaman sa = inbound ? tcp->tcp_sig_sa_in : tcp->tcp_sig_sa_out;
12691edba515SAndy Fiddaman if (sa == NULL) {
127054819d46SAndy Fiddaman if (!tcpsig_sa_exists(tcp, inbound, &sa)) {
12711edba515SAndy Fiddaman TCP_STAT(tcps, tcp_sig_match_failed);
12721edba515SAndy Fiddaman return (false);
12731edba515SAndy Fiddaman }
12741edba515SAndy Fiddaman
12751edba515SAndy Fiddaman /*
127654819d46SAndy Fiddaman * tcpsig_sa_exists() returns a held SA, so we don't need to
127754819d46SAndy Fiddaman * take another hold before adding it to tcp.
12781edba515SAndy Fiddaman */
12791edba515SAndy Fiddaman if (inbound)
12801edba515SAndy Fiddaman tcp->tcp_sig_sa_in = sa;
12811edba515SAndy Fiddaman else
12821edba515SAndy Fiddaman tcp->tcp_sig_sa_out = sa;
12831edba515SAndy Fiddaman }
12841edba515SAndy Fiddaman
1285*c2cbc6b8SAndy Fiddaman tcpsig_sa_touch(sa);
1286*c2cbc6b8SAndy Fiddaman
12871edba515SAndy Fiddaman VERIFY3U(sa->ts_key.sak_algid, ==, SADB_AALG_MD5);
12881edba515SAndy Fiddaman
12891edba515SAndy Fiddaman /* We have a key for this connection, generate the hash */
12901edba515SAndy Fiddaman MD5Init(&context);
12911edba515SAndy Fiddaman
12921edba515SAndy Fiddaman /* TCP pseudo-header */
12931edba515SAndy Fiddaman if (connp->conn_ipversion == IPV6_VERSION)
12941edba515SAndy Fiddaman tcpsig_pseudo_compute6(tcp, tcplen, &context, inbound);
12951edba515SAndy Fiddaman else
12961edba515SAndy Fiddaman tcpsig_pseudo_compute4(tcp, tcplen, &context, inbound);
12971edba515SAndy Fiddaman
12981edba515SAndy Fiddaman /* TCP header, excluding options and with a zero checksum */
12991edba515SAndy Fiddaman uint16_t offset = tcpha->tha_offset_and_reserved;
13001edba515SAndy Fiddaman uint16_t sum = tcpha->tha_sum;
13011edba515SAndy Fiddaman
13021edba515SAndy Fiddaman if (!inbound) {
13031edba515SAndy Fiddaman /* Account for the MD5 option we are going to add */
13041edba515SAndy Fiddaman tcpha->tha_offset_and_reserved += (5 << 4);
13051edba515SAndy Fiddaman }
13061edba515SAndy Fiddaman tcpha->tha_sum = 0;
13071edba515SAndy Fiddaman MD5Update(&context, tcpha, sizeof (*tcpha));
13081edba515SAndy Fiddaman tcpha->tha_offset_and_reserved = offset;
13091edba515SAndy Fiddaman tcpha->tha_sum = sum;
13101edba515SAndy Fiddaman
13111edba515SAndy Fiddaman /* TCP segment data */
13121edba515SAndy Fiddaman for (; mp != NULL; mp = mp->b_cont)
13131edba515SAndy Fiddaman MD5Update(&context, mp->b_rptr, mp->b_wptr - mp->b_rptr);
13141edba515SAndy Fiddaman
13151edba515SAndy Fiddaman /* Connection-specific key */
13161edba515SAndy Fiddaman MD5Update(&context, sa->ts_key.sak_key, sa->ts_key.sak_keylen);
13171edba515SAndy Fiddaman
13181edba515SAndy Fiddaman MD5Final(digest, &context);
13191edba515SAndy Fiddaman
13201edba515SAndy Fiddaman return (true);
13211edba515SAndy Fiddaman }
13221edba515SAndy Fiddaman
13231edba515SAndy Fiddaman bool
tcpsig_verify(mblk_t * mp,tcp_t * tcp,tcpha_t * tcpha,ip_recv_attr_t * ira,uint8_t * digest)13241edba515SAndy Fiddaman tcpsig_verify(mblk_t *mp, tcp_t *tcp, tcpha_t *tcpha, ip_recv_attr_t *ira,
13251edba515SAndy Fiddaman uint8_t *digest)
13261edba515SAndy Fiddaman {
13271edba515SAndy Fiddaman uint8_t calc_digest[MD5_DIGEST_LENGTH];
13281edba515SAndy Fiddaman
13291edba515SAndy Fiddaman if (!tcpsig_signature(mp, tcp, tcpha,
13301edba515SAndy Fiddaman ira->ira_pktlen - ira->ira_ip_hdr_length, calc_digest, true)) {
13311edba515SAndy Fiddaman /* The appropriate stat will already have been bumped */
13321edba515SAndy Fiddaman return (false);
13331edba515SAndy Fiddaman }
13341edba515SAndy Fiddaman
13351edba515SAndy Fiddaman if (bcmp(digest, calc_digest, sizeof (calc_digest)) != 0) {
13361edba515SAndy Fiddaman TCP_STAT(tcp->tcp_tcps, tcp_sig_verify_failed);
13371edba515SAndy Fiddaman return (false);
13381edba515SAndy Fiddaman }
13391edba515SAndy Fiddaman
13401edba515SAndy Fiddaman return (true);
13411edba515SAndy Fiddaman }
1342