Good Day,I am trying to enable the RFC 4895 sctp-auth extension in our communications framework. For this goal I am using standard linux sockets options, setting SCTP_AUTH_XXXX options to my client and server sockets.
The problem I have is when using endpoint shared keys. Although I am able to install it via SCTP_AUTH_KEY option I am NOT able to activate it via SCTP_AUTH_ACTIVE_KEY. I have tried multiple times setting the option at different points of time but I still get the "Invalid Argument" error.
I have created a minimal example where I can reproduce the problem in question. Note that I have not payed much attention to the correct tx and rx paths since the example is enough to illustrate the problem. I have some pcaps as well to understand what goes into the wire. In the one, and only, message that sends the client, I can see that in the shared key id field, AUTH chunk, 0 is being used which means no shared key. What I would like to have is to activate my shared key. Unfortunately I did not find a way to verify which shared key is being used except the field in AUTH chunk.
I have also tried tried use sendmsg() sys call with control data setting SCTP_AUTHINFO but it also does not work so I am out of ideas.
How exactly do I activate an endpoint shared key? P.S. See attached utils.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netinet/sctp.h> #include <unistd.h> #include <fcntl.h> #define RECVBUFSIZE 2048 int main() { char message[] = {"hello"}; int sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP); if(sd == -1) { perror("failure opening socket"); exit(EXIT_FAILURE); } { int on = 1; if (setsockopt(sd, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(on)) < 0) { perror("fail to set SCTP_NODELAY"); exit(EXIT_FAILURE); } } { sctp_initmsg initmsg; initmsg.sinit_num_ostreams = 2; initmsg.sinit_max_instreams = 2; initmsg.sinit_max_attempts = 10; initmsg.sinit_max_init_timeo = 1000; if (setsockopt(sd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg)) < 0) { perror("fail to set SCTP_INITMSG"); exit(EXIT_FAILURE); } } { int flags; if ((flags = fcntl(sd, F_GETFL, 0)) < 0) { perror("fail to get flags"); exit(EXIT_FAILURE); } flags |= SOCK_NONBLOCK; if (fcntl(sd, F_SETFL, 0) < 0) { perror("fail to set flags"); exit(EXIT_FAILURE); } } { int flags; if ((flags = fcntl(sd, F_GETFL, 0)) < 0) { perror("fail to get flags"); exit(EXIT_FAILURE); } flags |= SOCK_NONBLOCK; if (fcntl(sd, F_SETFL, 0) < 0) { perror("fail to set flags"); exit(EXIT_FAILURE); } } { struct sctp_assoc_value asso_auth_opt; asso_auth_opt.assoc_id = SCTP_FUTURE_ASSOC; asso_auth_opt.assoc_value = sd; if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_SUPPORTED, &asso_auth_opt, sizeof(asso_auth_opt)) < 0) { perror("fail to set SCTP_AUTH_SUPPORTED"); exit(EXIT_FAILURE); } struct sctp_authchunk datachunktype; datachunktype.sauth_chunk = 0; if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_CHUNK, &datachunktype, sizeof(sctp_authchunk)) < 0) { perror("fail to set SCTP_AUTH_CHUNK"); exit(EXIT_FAILURE); } uint8_t numhmacs = 2; uint32_t hmacbuflen = sizeof(sctp_hmacalgo) + (numhmacs * sizeof(uint16_t)); char hmacbuf[hmacbuflen]; struct sctp_hmacalgo *opthmac = (struct sctp_hmacalgo *) hmacbuf; opthmac->shmac_num_idents = numhmacs; opthmac->shmac_idents[0] = SCTP_AUTH_HMAC_ID_SHA256; opthmac->shmac_idents[1] = SCTP_AUTH_HMAC_ID_SHA1; if (setsockopt(sd, SOL_SCTP, SCTP_HMAC_IDENT, opthmac, hmacbuflen) < 0) { perror("fail to set SCTP_HMAC_IDENT"); exit(EXIT_FAILURE); } #if 1 int keylen = 5; uint32_t keybuflen = keylen + sizeof(sctp_authkey); char keybuf[keybuflen]; struct sctp_authkey *opt_key = (struct sctp_authkey *)keybuf; opt_key->sca_assoc_id = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something opt_key->sca_keylength = keylen; opt_key->sca_keynumber = 1; memcpy(opt_key->sca_key, "mykey", keylen); if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_KEY, &opt_key, keybuflen) < 0) { perror("fail to set SCTP_AUTH_KEY"); exit(EXIT_FAILURE); } #endif #if 0 struct sctp_authkeyid actkey; actkey.scact_assoc_id = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something actkey.scact_keynumber = 1; if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_ACTIVE_KEY, &actkey, sizeof(sctp_authkeyid)) < 0) { perror("fail to set SCTP_AUTH_ACTIVE_KEY"); exit(EXIT_FAILURE); } #endif } { int on = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { perror("fail to set SO_REUSEADDR"); exit(EXIT_FAILURE); } } { int on = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) { perror("fail to set SO_REUSEPORT"); exit(EXIT_FAILURE); } } struct sockaddr_in address; bzero((void*)&address, sizeof(address)); address.sin_family = AF_INET; address.sin_port = htons(14242); address.sin_addr.s_addr = inet_addr("127.0.0.1"); int rb = bind(sd, (struct sockaddr *)&address, sizeof(address)); if(rb == -1) { perror("bind error"); close(sd); exit(EXIT_FAILURE); } #if 0 struct sctp_authkeyid actkey; actkey.scact_assoc_id = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something actkey.scact_keynumber = 1; if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_ACTIVE_KEY, &actkey, sizeof(sctp_authkeyid)) < 0) { perror("fail to set SCTP_AUTH_ACTIVE_KEY"); exit(EXIT_FAILURE); } #endif int ret = connect(sd, (sockaddr *)&address, sizeof(address)); if (ret < 0) { perror("fail to connect"); close(sd); exit(EXIT_FAILURE); } #if 0 struct sctp_authkeyid actkey; actkey.scact_assoc_id = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something actkey.scact_keynumber = 1; if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_ACTIVE_KEY, &actkey, sizeof(sctp_authkeyid)) < 0) { perror("fail to set SCTP_AUTH_ACTIVE_KEY"); exit(EXIT_FAILURE); } #endif sctp_event_subscribe events = {0}; events.sctp_association_event = 1; events.sctp_data_io_event = 1; events.sctp_shutdown_event = 1; events.sctp_peer_error_event = 1; if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events)) < 0) { perror("fail to set SCTP_EVENTS"); exit(EXIT_FAILURE); } sctp_paddrparams heartbeats = {0}; heartbeats.spp_flags = SPP_HB_ENABLE | SPP_PMTUD_DISABLE; heartbeats.spp_hbinterval = 5000; heartbeats.spp_pathmaxrxt = 1; if (setsockopt(sd, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &heartbeats, sizeof(heartbeats)) < 0) { perror("fail to set SCTP_EVENTS"); exit(EXIT_FAILURE); } sctp_rtoinfo rtoinfo = {0}; int sctprto = 200; rtoinfo.srto_min = sctprto; rtoinfo.srto_max = 2 * sctprto; rtoinfo.srto_initial = (3*sctprto)/2; if (setsockopt(sd, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo)) < 0) { perror("fail to set SCTP_EVENTS"); exit(EXIT_FAILURE); } int sent; #if 0 struct msghdr msg = {0}; struct iovec datav; datav.iov_base = message; datav.iov_len = sizeof(message); msg.msg_iovlen = 1; msg.msg_iov = &datav; char ctrlbuf[CMSG_SPACE(sizeof(sctp_sndrcvinfo)) + CMSG_SPACE(sizeof(sctp_authinfo))]; msg.msg_control = ctrlbuf; msg.msg_controllen = sizeof(ctrlbuf); struct cmsghdr *pcmsg = CMSG_FIRSTHDR(&msg); pcmsg->cmsg_len = CMSG_LEN(sizeof(sctp_sndrcvinfo)); pcmsg->cmsg_level = SOL_SCTP; pcmsg->cmsg_type = SCTP_SNDRCV; struct sctp_sndrcvinfo *srinfo = (struct sctp_sndrcvinfo *) CMSG_DATA(pcmsg); srinfo->sinfo_stream = 0; srinfo->sinfo_ppid = 11; pcmsg = CMSG_NXTHDR(&msg, pcmsg); pcmsg->cmsg_len = CMSG_LEN(sizeof(sctp_authinfo)); pcmsg->cmsg_level = SOL_SCTP; pcmsg->cmsg_type = SCTP_AUTHINFO; struct sctp_authinfo *authinfo = (struct sctp_authinfo *) CMSG_DATA(pcmsg); authinfo->auth_keynumber = 1; if ((sent = sendmsg(sd, &msg, MSG_DONTWAIT)) <= 0) { perror("fail to sendmsg"); #else sctp_sndrcvinfo sinfo = {0}; sinfo.sinfo_ppid = 11; sinfo.sinfo_stream = 0; if ((sent = sctp_send(sd, message, sizeof(message), &sinfo, 0)) <= 0) { perror("fail to sctp_send"); #endif exit(EXIT_FAILURE); } fprintf(stdout, "sent %s/%d\n", message, sent); close(sd); return EXIT_SUCCESS; }
Attachment:
sctp-auth-default-ok.pcapng
Description: application/pcapng
Attachment:
sctp-auth-share-key-fail-1.pcapng
Description: application/pcapng
Attachment:
sctp-auth-share-key-fail-2.pcapng
Description: application/pcapng
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/sctp.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #define RECVBUFSIZE 2048 int main() { int sd; sd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP); if(sd == -1) { perror("failure opening socket"); exit(EXIT_FAILURE); } struct sockaddr_in serverAddress; bzero((void *)&serverAddress, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = htonl(0x7f000001); serverAddress.sin_port = htons(14242); { int on = 1; if (setsockopt(sd, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(on)) < 0) { perror("fail to set SCTP_NODELAY"); exit(EXIT_FAILURE); } } { sctp_initmsg initmsg; initmsg.sinit_num_ostreams = 2; initmsg.sinit_max_instreams = 2; initmsg.sinit_max_attempts = 10; initmsg.sinit_max_init_timeo = 1000; if (setsockopt(sd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg)) < 0) { perror("fail to set SCTP_INITMSG"); exit(EXIT_FAILURE); } } { int flags; if ((flags = fcntl(sd, F_GETFL, 0)) < 0) { perror("fail to get flags"); exit(EXIT_FAILURE); } flags |= SOCK_NONBLOCK; if (fcntl(sd, F_SETFL, 0) < 0) { perror("fail to set flags"); exit(EXIT_FAILURE); } } { struct sctp_assoc_value asso_auth_opt; asso_auth_opt.assoc_id = SCTP_FUTURE_ASSOC; asso_auth_opt.assoc_value = sd; if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_SUPPORTED, &asso_auth_opt, sizeof(asso_auth_opt)) < 0) { perror("fail to set SCTP_AUTH_SUPPORTED"); exit(EXIT_FAILURE); } struct sctp_authchunk datachunktype; datachunktype.sauth_chunk = 0; if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_CHUNK, &datachunktype, sizeof(sctp_authchunk)) < 0) { perror("fail to set SCTP_AUTH_CHUNK"); exit(EXIT_FAILURE); } uint8_t numhmacs = 2; uint32_t hmacbuflen = sizeof(sctp_hmacalgo) + (numhmacs * sizeof(uint16_t)); char hmacbuf[hmacbuflen]; struct sctp_hmacalgo *opthmac = (struct sctp_hmacalgo *) hmacbuf; opthmac->shmac_num_idents = numhmacs; opthmac->shmac_idents[0] = SCTP_AUTH_HMAC_ID_SHA256; opthmac->shmac_idents[1] = SCTP_AUTH_HMAC_ID_SHA1; if (setsockopt(sd, SOL_SCTP, SCTP_HMAC_IDENT, opthmac, hmacbuflen) < 0) { perror("fail to set SCTP_HMAC_IDENT"); exit(EXIT_FAILURE); } #if 1 int keylen = 5; uint32_t keybuflen = keylen + sizeof(sctp_authkey); char keybuf[keybuflen]; struct sctp_authkey *opt_key = (struct sctp_authkey *)keybuf; opt_key->sca_assoc_id = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something opt_key->sca_keylength = keylen; opt_key->sca_keynumber = 1; memcpy(opt_key->sca_key, "mykey", keylen); if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_KEY, &opt_key, keybuflen) < 0) { perror("fail to set SCTP_AUTH_KEY"); exit(EXIT_FAILURE); } #endif } { int on = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) { perror("fail to set SO_REUSEPORT"); exit(EXIT_FAILURE); } } int rb = bind(sd, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); if(rb == -1) { perror("bind error"); close(sd); exit(EXIT_FAILURE); } int rl = listen(sd, 1); if(rl < 0) { perror("failed to listen for connection"); close(sd); exit(EXIT_FAILURE); } char buf[RECVBUFSIZE]; struct sctp_sndrcvinfo sinfo = {0}; int newsd, rcvdlen; for(;;) { struct sockaddr_in client_addr = {0}; socklen_t socklen = sizeof(client_addr); newsd = accept(sd, (sockaddr*)&client_addr, &socklen); if (newsd <= 0) { switch (errno) { case EWOULDBLOCK: perror("fail to accept block"); return EXIT_FAILURE; case EINTR: continue; default: perror("fail to sctp_recvmsg"); return EXIT_FAILURE; } } { int flags; if ((flags = fcntl(newsd, F_GETFL, 0)) < 0) { perror("fail to get flags"); exit(EXIT_FAILURE); } flags |= SOCK_NONBLOCK; if (fcntl(newsd, F_SETFL, 0) < 0) { perror("fail to set flags"); exit(EXIT_FAILURE); } sctp_event_subscribe events = {0}; events.sctp_association_event = 1; events.sctp_data_io_event = 1; events.sctp_shutdown_event = 1; events.sctp_peer_error_event = 1; if (setsockopt(newsd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events)) < 0) { perror("fail to set SCTP_EVENTS"); exit(EXIT_FAILURE); } sctp_paddrparams heartbeats = {0}; heartbeats.spp_flags = SPP_HB_ENABLE | SPP_PMTUD_DISABLE; heartbeats.spp_hbinterval = 5000; heartbeats.spp_pathmaxrxt = 1; if (setsockopt(newsd, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &heartbeats, sizeof(heartbeats)) < 0) { perror("fail to set SCTP_EVENTS"); exit(EXIT_FAILURE); } sctp_rtoinfo rtoinfo = {0}; int sctprto = 200; rtoinfo.srto_min = sctprto; rtoinfo.srto_max = 2 * sctprto; rtoinfo.srto_initial = (3*sctprto)/2; if (setsockopt(newsd, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo)) < 0) { perror("fail to set SCTP_EVENTS"); exit(EXIT_FAILURE); } int rflags; while (1) { if ((rcvdlen = sctp_recvmsg(newsd, buf, sizeof(buf), 0, 0, &sinfo, &rflags)) <= 0) { switch (errno) { case EWOULDBLOCK: perror("fail to sctp_recvmsg woudlblock"); return EXIT_FAILURE; case EINTR: continue; default: perror("fail to sctp_recvmsg"); return EXIT_FAILURE; } } if (rflags & MSG_NOTIFICATION) { fprintf(stdout, "sctp event - contine\n"); continue; } fprintf(stdout, "rcvd %d\n", rcvdlen); if (rcvdlen) { buf[rcvdlen] = 0; fprintf(stdout, "rcvd msg %s bytes %d\n", buf, rcvdlen); } } } } return EXIT_SUCCESS; }