From 848516c51f3cd6b6a9e869565137f3e87a55ff3b Mon Sep 17 00:00:00 2001 From: jolavillette Date: Mon, 19 Jan 2026 10:10:03 +0100 Subject: [PATCH 1/4] Fix Windows compilation: correct header order, socket types, and format strings --- src/libsam3/libsam3.c | 27 ++++++++++++++++++++------- src/libsam3a/libsam3a.c | 16 +++++++++++----- src/libsam3a/libsam3a.h | 2 +- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/libsam3/libsam3.c b/src/libsam3/libsam3.c index ab0cd3e..91b1277 100644 --- a/src/libsam3/libsam3.c +++ b/src/libsam3/libsam3.c @@ -38,8 +38,8 @@ #ifdef __MINGW32__ //#include -#include #include +#include #include #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 @@ -47,6 +47,7 @@ #ifndef SHUT_RDWR #define SHUT_RDWR 2 #endif +#define close closesocket #endif #if defined(__unix__) || defined(__APPLE__) @@ -96,8 +97,14 @@ int sam3tcpSetTimeoutSend(int fd, int timeoutms) { struct timeval tv; // ms2timeval(&tv, timeoutms); - return (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 ? -1 +#ifdef _WIN32 + DWORD timeout = timeoutms; + return (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)) < 0 ? -1 + : 0); +#else + return (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)) < 0 ? -1 : 0); +#endif } return -1; } @@ -107,8 +114,14 @@ int sam3tcpSetTimeoutReceive(int fd, int timeoutms) { struct timeval tv; // ms2timeval(&tv, timeoutms); - return (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ? -1 +#ifdef _WIN32 + DWORD timeout = timeoutms; + return (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)) < 0 ? -1 + : 0); +#else + return (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0 ? -1 : 0); +#endif } return -1; } @@ -148,7 +161,7 @@ int sam3tcpConnectIP(uint32_t ip, int port) { } } // - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const char*)&val, sizeof(val)); // if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { if (libsam3_debug) @@ -931,7 +944,7 @@ int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, strlen(v) < SAM3_PRIVKEY_MIN_SIZE) { if (libsam3_debug) fprintf(stderr, "sam3CreateSession: invalid reply (%ld)...\n", - (v != NULL ? strlen(v) : -1)); + (long)(v != NULL ? strlen(v) : -1)); if (libsam3_debug) sam3DumpFieldList(rep); sam3FreeFieldList(rep); @@ -939,7 +952,7 @@ int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, } // save our keys if (strlen(v) > SAM3_PRIVKEY_MAX_SIZE) { - fprintf(stderr, "ERROR, Unexpected key size (%li)!\n", strlen(v)); + fprintf(stderr, "ERROR, Unexpected key size (%li)!\n", (long)strlen(v)); goto error; } strcpy(ses->privkey, v); @@ -955,7 +968,7 @@ int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, !sam3CheckValidKeyLength(v)) { if (libsam3_debug) fprintf(stderr, "sam3CreateSession: invalid NAMING reply (%ld)...\n", - (v != NULL ? strlen(v) : -1)); + (long)(v != NULL ? strlen(v) : -1)); if (libsam3_debug) sam3DumpFieldList(rep); sam3FreeFieldList(rep); diff --git a/src/libsam3a/libsam3a.c b/src/libsam3a/libsam3a.c index 3811489..f4d0b8b 100644 --- a/src/libsam3a/libsam3a.c +++ b/src/libsam3a/libsam3a.c @@ -37,8 +37,8 @@ #ifdef __MINGW32__ //#include -#include #include +#include #include #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 @@ -46,6 +46,8 @@ #ifndef SHUT_RDWR #define SHUT_RDWR 2 #endif +#define close closesocket +#define ioctl ioctlsocket #endif #if defined(__unix__) && !defined(__APPLE__) @@ -150,11 +152,15 @@ static int sam3aSocketSetTimeoutReceive (int fd, int timeoutms) { */ static int sam3aBytesAvail(int fd) { +#ifdef _WIN32 + u_long av = 0; +#else int av = 0; +#endif // if (ioctl(fd, FIONREAD, &av) < 0) return -1; - return av; + return (int)av; } static uint32_t sam3aResolveHost(const char *hostname) { @@ -183,7 +189,7 @@ static int sam3aConnect(uint32_t ip, int port, int *complete) { if ((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0) return -1; // - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const char*)&val, sizeof(val)); // for (;;) { struct sockaddr_in addr; @@ -1044,7 +1050,7 @@ static void aioSesConnected(Sam3ASession *ses) { int res; socklen_t len = sizeof(res); // - if (getsockopt(ses->fd, SOL_SOCKET, SO_ERROR, &res, &len) == 0 && res == 0) { + if (getsockopt(ses->fd, SOL_SOCKET, SO_ERROR, (char*)&res, &len) == 0 && res == 0) { // ok, connected if (sam3aSesStartHandshake(ses, NULL) < 0) sesError(ses, NULL); @@ -1500,7 +1506,7 @@ static void aioConnConnected(Sam3AConnection *conn) { int res; socklen_t len = sizeof(res); // - if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &res, &len) == 0 && res == 0) { + if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, (char*)&res, &len) == 0 && res == 0) { // ok, connected if (sam3aConnStartHandshake(conn, NULL) < 0) connError(conn, NULL); diff --git a/src/libsam3a/libsam3a.h b/src/libsam3a/libsam3a.h index 85f2d9b..30cb35c 100644 --- a/src/libsam3a/libsam3a.h +++ b/src/libsam3a/libsam3a.h @@ -35,8 +35,8 @@ #ifdef __MINGW32__ //#include -#include #include +#include #include //#define SOCK_CLOEXEC O_CLOEXEC //#define SOCK_NONBLOCK O_NONBLOCK From c49544dda84caaa358f4921b3199c0e7402f947a Mon Sep 17 00:00:00 2001 From: jolavillette Date: Mon, 23 Feb 2026 15:11:57 +0100 Subject: [PATCH 2/4] libsam3: Fix buffer overflows and support modern large I2P destinations --- src/libsam3/libsam3.c | 31 ++++++++++++------- src/libsam3/libsam3.h | 10 +++---- src/libsam3a/libsam3a.c | 66 +++++++++++++++++++++++------------------ src/libsam3a/libsam3a.h | 16 +++++----- 4 files changed, 71 insertions(+), 52 deletions(-) diff --git a/src/libsam3/libsam3.c b/src/libsam3/libsam3.c index 91b1277..108fe97 100644 --- a/src/libsam3/libsam3.c +++ b/src/libsam3/libsam3.c @@ -568,7 +568,7 @@ SAMFieldList *sam3ParseReply(const char *rep) { // first item is always 2-word reply, with first word in name and second in // value SAMFieldList *sam3ReadReply(int fd) { - char rep[2048]; // should be enough for any reply + char rep[8192]; // should be enough for any reply // if (sam3tcpReceiveStr(fd, rep, sizeof(rep)) < 0) return NULL; @@ -784,9 +784,11 @@ int sam3GenerateKeys(Sam3Session *ses, const char *hostname, int port, strcpyerr(ses, "PRIVKEY_ERROR"); } const char *pub = sam3FindField(rep, "PUB"); - strcpy(ses->pubkey, pub); + strncpy(ses->pubkey, pub, sizeof(ses->pubkey) - 1); + ses->pubkey[sizeof(ses->pubkey) - 1] = 0; const char *priv = sam3FindField(rep, "PRIV"); - strcpy(ses->privkey, priv); + strncpy(ses->privkey, priv, sizeof(ses->privkey) - 1); + ses->privkey[sizeof(ses->privkey) - 1] = 0; res = 0; // sam3FreeFieldList(rep); @@ -817,7 +819,8 @@ int sam3NameLookup(Sam3Session *ses, const char *hostname, int port, // if (strcmp(rs, "OK") == 0) { if (pub != NULL && sam3CheckValidKeyLength(pub)) { - strcpy(ses->destkey, pub); + strncpy(ses->destkey, pub, sizeof(ses->destkey) - 1); + ses->destkey[sizeof(ses->destkey) - 1] = 0; strcpyerr(ses, NULL); res = 0; } @@ -955,7 +958,8 @@ int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, fprintf(stderr, "ERROR, Unexpected key size (%li)!\n", (long)strlen(v)); goto error; } - strcpy(ses->privkey, v); + strncpy(ses->privkey, v, sizeof(ses->privkey) - 1); + ses->privkey[sizeof(ses->privkey) - 1] = 0; sam3FreeFieldList(rep); // get public key if (sam3tcpPrintf(ses->fd, "NAMING LOOKUP NAME=ME\n") < 0) @@ -974,7 +978,8 @@ int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, sam3FreeFieldList(rep); goto error; } - strcpy(ses->pubkey, v); + strncpy(ses->pubkey, v, sizeof(ses->pubkey) - 1); + ses->pubkey[sizeof(ses->pubkey) - 1] = 0; sam3FreeFieldList(rep); // if (libsam3_debug) @@ -1042,7 +1047,8 @@ Sam3Connection *sam3StreamConnect(Sam3Session *ses, const char *destkey) { } sam3FreeFieldList(rep); if (conn != NULL) { - strcpy(conn->destkey, destkey); + strncpy(conn->destkey, destkey, sizeof(conn->destkey) - 1); + conn->destkey[sizeof(conn->destkey) - 1] = 0; conn->ses = ses; conn->next = ses->connlist; ses->connlist = conn; @@ -1059,7 +1065,7 @@ Sam3Connection *sam3StreamConnect(Sam3Session *ses, const char *destkey) { Sam3Connection *sam3StreamAccept(Sam3Session *ses) { if (ses != NULL) { SAMFieldList *rep = NULL; - char repstr[1024]; + char repstr[8192]; Sam3Connection *conn; // if (ses->type != SAM3_SESSION_STREAM) { @@ -1110,7 +1116,8 @@ Sam3Connection *sam3StreamAccept(Sam3Session *ses) { goto error; } sam3FreeFieldList(rep); - strcpy(conn->destkey, repstr); + strncpy(conn->destkey, repstr, sizeof(conn->destkey) - 1); + conn->destkey[sizeof(conn->destkey) - 1] = 0; conn->ses = ses; conn->next = ses->connlist; ses->connlist = conn; @@ -1248,8 +1255,10 @@ ssize_t sam3DatagramReceive(Sam3Session *ses, void *buf, size_t bufsize) { } // if ((v = sam3FindField(rep, "DESTINATION")) != NULL && - sam3CheckValidKeyLength(v)) - strncpy(ses->destkey, v, sizeof(ses->destkey)); + sam3CheckValidKeyLength(v)) { + strncpy(ses->destkey, v, sizeof(ses->destkey) - 1); + ses->destkey[sizeof(ses->destkey) - 1] = 0; + } v = sam3FindField(rep, "SIZE"); // we have this field -- for sure if (!v[0] || !isdigit(*v)) { strcpyerr(ses, "I2P_ERROR_SIZE"); diff --git a/src/libsam3/libsam3.h b/src/libsam3/libsam3.h index 316e122..488d4bd 100644 --- a/src/libsam3/libsam3.h +++ b/src/libsam3/libsam3.h @@ -53,9 +53,9 @@ extern int libsam3_debug; #define SAM3_DESTINATION_TRANSIENT (NULL) #define SAM3_PUBKEY_SIZE (516) -#define SAM3_CERT_SIZE (100) -#define SAM3_PRIVKEY_MIN_SIZE (884) -#define SAM3_PRIVKEY_MAX_SIZE (1024) +#define SAM3_CERT_SIZE (3580) +#define SAM3_PRIVKEY_MIN_SIZE (256) +#define SAM3_PRIVKEY_MAX_SIZE (8192) //////////////////////////////////////////////////////////////////////////////// /* returns fd or -1 */ @@ -160,7 +160,7 @@ typedef struct Sam3Session { char destkey[SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE + 1]; // for DGRAM sessions (asciiz) // int destsig; - char error[32]; // error message (asciiz) + char error[256]; // error message (asciiz) uint32_t ip; int port; // this will be changed to UDP port for DRAM/RAW (can be 0) struct Sam3Connection *connlist; // list of opened connections @@ -175,7 +175,7 @@ typedef struct Sam3Connection { char destkey[SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE + 1]; // remote destination public key (asciiz) int destcert; - char error[32]; // error message (asciiz) + char error[256]; // error message (asciiz) } Sam3Connection; //////////////////////////////////////////////////////////////////////////////// diff --git a/src/libsam3a/libsam3a.c b/src/libsam3a/libsam3a.c index f4d0b8b..d2da2c0 100644 --- a/src/libsam3a/libsam3a.c +++ b/src/libsam3a/libsam3a.c @@ -106,8 +106,8 @@ static inline int isValidKeyChar(char ch) { } int sam3aIsValidPubKey(const char *key) { - if (key != NULL && strlen(key) == SAM3A_PUBKEY_SIZE) { - for (int f = 0; f < SAM3A_PUBKEY_SIZE; ++f) + if (key != NULL && strlen(key) >= SAM3A_PUBKEY_SIZE) { + for (int f = 0; f < (int)strlen(key); ++f) if (!isValidKeyChar(key[f])) return 0; return 1; @@ -116,8 +116,8 @@ int sam3aIsValidPubKey(const char *key) { } int sam3aIsValidPrivKey(const char *key) { - if (key != NULL && strlen(key) == SAM3A_PRIVKEY_SIZE) { - for (int f = 0; f < SAM3A_PRIVKEY_SIZE; ++f) + if (key != NULL && strlen(key) >= SAM3A_PRIVKEY_SIZE) { + for (int f = 0; f < (int)strlen(key); ++f) if (!isValidKeyChar(key[f])) return 0; return 1; @@ -900,18 +900,18 @@ static void aioSesCmdSender(Sam3ASession *ses) { // if (ses->aio.dataPos == ses->aio.dataUsed) { // hello sent, now wait for reply - // 2048 bytes of reply line should be enough - if (ses->aio.dataSize < 2049) { - char *n = realloc(ses->aio.data, 2049); + // 8192 bytes of reply line should be enough + if (ses->aio.dataSize < 8193) { + char *n = realloc(ses->aio.data, 8193); // if (n == NULL) { sesError(ses, "MEMORY_ERROR"); return; } ses->aio.data = n; - ses->aio.dataSize = 2049; + ses->aio.dataSize = 8193; } - ses->aio.dataUsed = 2048; + ses->aio.dataUsed = 8192; ses->aio.dataPos = 0; ses->cbAIOProcessorR = aioSesCmdReplyReader; ses->cbAIOProcessorW = NULL; @@ -986,7 +986,7 @@ static void aioSesNameMeChecker(Sam3ASession *ses) { } if (!sam3aIsGoodReply(rep, "NAMING", "REPLY", "RESULT", "OK") || (v = sam3aFindField(rep, "VALUE")) == NULL || - strlen(v) != SAM3A_PUBKEY_SIZE) { + strlen(v) < SAM3A_PUBKEY_SIZE) { // if (libsam3a_debug) fprintf(stderr, "sam3aCreateSession: invalid NAMING // reply (%d)...\n", (v != NULL ? strlen(v) : -1)); if ((v = sam3aFindField(rep, "RESULT")) != NULL && strcmp(v, "OK") == 0) @@ -995,7 +995,8 @@ static void aioSesNameMeChecker(Sam3ASession *ses) { sam3aFreeFieldList(rep); return; } - strcpy(ses->pubkey, v); + strncpy(ses->pubkey, v, sizeof(ses->pubkey) - 1); + ses->pubkey[sizeof(ses->pubkey) - 1] = 0; sam3aFreeFieldList(rep); // ses->cbAIOProcessorR = ses->cbAIOProcessorW = NULL; @@ -1014,7 +1015,7 @@ static void aioSesCreateChecker(Sam3ASession *ses) { } if (!sam3aIsGoodReply(rep, "SESSION", "STATUS", "RESULT", "OK") || (v = sam3aFindField(rep, "DESTINATION")) == NULL || - strlen(v) != SAM3A_PRIVKEY_SIZE) { + strlen(v) < SAM3A_PRIVKEY_SIZE) { sam3aFreeFieldList(rep); if ((v = sam3aFindField(rep, "RESULT")) != NULL && strcmp(v, "OK") == 0) v = NULL; @@ -1023,7 +1024,8 @@ static void aioSesCreateChecker(Sam3ASession *ses) { } // ok // fprintf(stderr, "\nPK: %s\n", v); - strcpy(ses->privkey, v); + strncpy(ses->privkey, v, sizeof(ses->privkey) - 1); + ses->privkey[sizeof(ses->privkey) - 1] = 0; sam3aFreeFieldList(rep); // get our public key if (aioSesSendCmdWaitReply(ses, aioSesNameMeChecker, "%s\n", @@ -1082,7 +1084,8 @@ int sam3aCreateSessionEx(Sam3ASession *ses, const Sam3ASessionCallbacks *cb, goto error; if (privkey == NULL) privkey = "TRANSIENT"; - strcpy(ses->privkey, privkey); + strncpy(ses->privkey, privkey, sizeof(ses->privkey) - 1); + ses->privkey[sizeof(ses->privkey) - 1] = 0; if (params != NULL && (ses->params = strdup(params)) == NULL) goto error; ses->timeoutms = timeoutms; @@ -1157,10 +1160,12 @@ static void aioSesKeyGenChecker(Sam3ASession *ses) { const char *pub = sam3aFindField(rep, "PUB"), *priv = sam3aFindField(rep, "PRIV"); // - if (pub != NULL && strlen(pub) == SAM3A_PUBKEY_SIZE && priv != NULL && - strlen(priv) == SAM3A_PRIVKEY_SIZE) { - strcpy(ses->pubkey, pub); - strcpy(ses->privkey, priv); + if (pub != NULL && strlen(pub) >= SAM3A_PUBKEY_SIZE && priv != NULL && + strlen(priv) >= SAM3A_PRIVKEY_SIZE) { + strncpy(ses->pubkey, pub, sizeof(ses->pubkey) - 1); + ses->pubkey[sizeof(ses->pubkey) - 1] = 0; + strncpy(ses->privkey, priv, sizeof(ses->privkey) - 1); + ses->privkey[sizeof(ses->privkey) - 1] = 0; sam3aFreeFieldList(rep); if (ses->cb.cbCreated != NULL) ses->cb.cbCreated(ses); @@ -1229,8 +1234,9 @@ static void aioSesNameResChecker(Sam3ASession *ses) { *pub = sam3aFindField(rep, "VALUE"); // if (strcmp(rs, "OK") == 0) { - if (pub != NULL && strlen(pub) == SAM3A_PUBKEY_SIZE) { - strcpy(ses->destkey, pub); + if (pub != NULL && strlen(pub) >= SAM3A_PUBKEY_SIZE) { + strncpy(ses->destkey, pub, sizeof(ses->destkey) - 1); + ses->destkey[sizeof(ses->destkey) - 1] = 0; sam3aFreeFieldList(rep); if (ses->cb.cbCreated != NULL) ses->cb.cbCreated(ses); @@ -1324,18 +1330,18 @@ static void aioConnCmdSender(Sam3AConnection *conn) { // if (conn->aio.dataPos == conn->aio.dataUsed) { // hello sent, now wait for reply - // 2048 bytes of reply line should be enough - if (conn->aio.dataSize < 2049) { - char *n = realloc(conn->aio.data, 2049); + // 8192 bytes of reply line should be enough + if (conn->aio.dataSize < 8193) { + char *n = realloc(conn->aio.data, 8193); // if (n == NULL) { connError(conn, "MEMORY_ERROR"); return; } conn->aio.data = n; - conn->aio.dataSize = 2049; + conn->aio.dataSize = 8193; } - conn->aio.dataUsed = 2048; + conn->aio.dataUsed = 8192; conn->aio.dataPos = 0; conn->cbAIOProcessorR = aioConnCmdReplyReader; conn->cbAIOProcessorW = NULL; @@ -1558,14 +1564,15 @@ Sam3AConnection *sam3aStreamConnectEx(Sam3ASession *ses, const Sam3AConnectionCallbacks *cb, const char *destkey, int timeoutms) { if (sam3aIsActiveSession(ses) && ses->type == SAM3A_SESSION_STREAM && - destkey != NULL && strlen(destkey) == SAM3A_PUBKEY_SIZE) { + destkey != NULL && strlen(destkey) >= SAM3A_PUBKEY_SIZE) { Sam3AConnection *conn = calloc(1, sizeof(Sam3AConnection)); // if (conn == NULL) return NULL; if (cb != NULL) conn->cb = *cb; - strcpy(conn->destkey, destkey); + strncpy(conn->destkey, destkey, sizeof(conn->destkey) - 1); + conn->destkey[sizeof(conn->destkey) - 1] = 0; conn->timeoutms = timeoutms; // conn->aio.udata = aioConConnectHandshacked; @@ -1590,14 +1597,15 @@ Sam3AConnection *sam3aStreamConnectEx(Sam3ASession *ses, static void aioConnAcceptCheckerA(Sam3AConnection *conn) { SAMFieldList *rep = sam3aParseReply(conn->aio.data); // - if (rep != NULL || strlen(conn->aio.data) != SAM3A_PUBKEY_SIZE || + if (rep != NULL || strlen(conn->aio.data) < SAM3A_PUBKEY_SIZE || !sam3aIsValidPubKey(conn->aio.data)) { sam3aFreeFieldList(rep); connError(conn, NULL); return; } sam3aFreeFieldList(rep); - strcpy(conn->destkey, conn->aio.data); + strncpy(conn->destkey, conn->aio.data, sizeof(conn->destkey) - 1); + conn->destkey[sizeof(conn->destkey) - 1] = 0; conn->callDisconnectCB = 1; conn->cbAIOProcessorR = aioConnDataReader; conn->cbAIOProcessorW = aioConnDataWriter; diff --git a/src/libsam3a/libsam3a.h b/src/libsam3a/libsam3a.h index 30cb35c..7fab6b7 100644 --- a/src/libsam3a/libsam3a.h +++ b/src/libsam3a/libsam3a.h @@ -64,7 +64,9 @@ extern int libsam3a_debug; #define SAM3A_DESTINATION_TRANSIENT (NULL) #define SAM3A_PUBKEY_SIZE (516) -#define SAM3A_PRIVKEY_SIZE (884) +#define SAM3A_CERT_SIZE (3580) +#define SAM3A_PRIVKEY_SIZE (1024) +#define SAM3A_PRIVKEY_MAX_SIZE (8192) //////////////////////////////////////////////////////////////////////////////// extern uint64_t sam3atimeval2ms(const struct timeval *tv); @@ -114,11 +116,11 @@ struct Sam3ASession { Sam3ASessionType type; /** session type */ int fd; /** socket file descriptor */ int cancelled; /** fd was shutdown()ed, but not closed yet */ - char privkey[SAM3A_PRIVKEY_SIZE + 1]; /** private key (asciiz) */ - char pubkey[SAM3A_PUBKEY_SIZE + 1]; /** public key (asciiz) */ + char privkey[SAM3A_PRIVKEY_MAX_SIZE + 1]; /** private key (asciiz) */ + char pubkey[SAM3A_PUBKEY_SIZE + SAM3A_CERT_SIZE + 1]; /** public key (asciiz) */ char channel[66]; /** channel name (asciiz) */ - char destkey[SAM3A_PUBKEY_SIZE + 1]; /** for DGRAM sessions (asciiz) */ - char error[64]; /** error message (asciiz) */ + char destkey[SAM3A_PUBKEY_SIZE + SAM3A_CERT_SIZE + 1]; /** for DGRAM sessions (asciiz) */ + char error[256]; /** error message (asciiz) */ uint32_t ip; /** ipv4 address of sam api interface */ int port; /** UDP port for DRAM/RAW (can be 0) */ Sam3AConnection *connlist; /** list of opened connections */ @@ -166,8 +168,8 @@ struct Sam3AConnection { /** file descriptor */ int fd; int cancelled; // fd was shutdown()ed, but not closed yet - char destkey[SAM3A_PUBKEY_SIZE + 1]; // (asciiz) - char error[32]; // (asciiz) + char destkey[SAM3A_PUBKEY_SIZE + SAM3A_CERT_SIZE + 1]; // (asciiz) + char error[256]; // (asciiz) /** begin internal members */ // for async i/o From 902dfeb7a9847d133c71d25c6b1396489bf84af2 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Tue, 15 Sep 2026 07:45:39 +0200 Subject: [PATCH 3/4] libsam3a: portable non-blocking socket creation (macOS, Windows) sam3aConnect() passed SOCK_NONBLOCK | SOCK_CLOEXEC to socket(), which only exists on Linux. The macOS shim mapped SOCK_NONBLOCK to O_NONBLOCK, but socket() does not accept fcntl flags in its type argument, so the call failed with EINVAL; on Windows the flags are not defined at all. Use the atomic Linux form when both flags exist, otherwise create a plain socket and switch it to non-blocking mode with ioctlsocket(FIONBIO) on Windows or fcntl(O_NONBLOCK) plus FD_CLOEXEC elsewhere. The now-unused SOCK_NONBLOCK/SOCK_CLOEXEC shims for __APPLE__ are dropped; the MSG_NOSIGNAL define and TickCount() from the upstream macOS fixes are kept, as pre-11 macOS SDKs do not define MSG_NOSIGNAL. Co-Authored-By: Claude Fable 5.1 --- src/libsam3a/libsam3a.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/libsam3a/libsam3a.c b/src/libsam3a/libsam3a.c index d2da2c0..fc3b03b 100644 --- a/src/libsam3a/libsam3a.c +++ b/src/libsam3a/libsam3a.c @@ -63,16 +63,13 @@ #include #endif +#if defined(__unix__) || defined(__APPLE__) +#include +#endif + #if defined(__APPLE__) #include #include -#ifndef SOCK_CLOEXEC -#define SOCK_CLOEXEC 0 -#endif -#ifndef SOCK_NONBLOCK -#include -#define SOCK_NONBLOCK O_NONBLOCK -#endif #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif @@ -185,9 +182,34 @@ static int sam3aConnect(uint32_t ip, int port, int *complete) { if (ip == 0 || ip == 0xffffffffUL || port < 1 || port > 65535) return -1; // - // yes, this is Linux-specific; you know what? i don't care. +#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) + // Linux (and BSDs that support it): atomic non-blocking + close-on-exec if ((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0) return -1; +#else + // macOS and Windows have no SOCK_NONBLOCK/SOCK_CLOEXEC socket() flags: + // create a plain socket, then switch it to non-blocking mode. + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return -1; +#if defined(_WIN32) || defined(__MINGW32__) + { + u_long mode = 1; + if (ioctlsocket(fd, FIONBIO, &mode) != 0) { + close(fd); + return -1; + } + } +#else + { + int flags = fcntl(fd, F_GETFL, 0); + if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + close(fd); + return -1; + } + fcntl(fd, F_SETFD, FD_CLOEXEC); + } +#endif +#endif // setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const char*)&val, sizeof(val)); // From 412ab6e7ea9a3ca69dc7fed8ae67f025b4f922f5 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Tue, 15 Sep 2026 07:46:18 +0200 Subject: [PATCH 4/4] libsam3: do not dereference NULL keys in sam3GenerateKeys When the SAM router closes the connection or answers with an error, sam3ReadReply() returns NULL or a reply without PUB/PRIV fields. The function recorded the error but still copied the (NULL) fields into the session and returned success, crashing the caller. Validate the reply, the public key length and the private key size before touching the session buffers, and fail with -1 otherwise. Also reject an out-of-range sigType instead of indexing past the sigtypes table. Co-Authored-By: Claude Fable 5.1 --- src/libsam3/libsam3.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/libsam3/libsam3.c b/src/libsam3/libsam3.c index 108fe97..5cf27a3 100644 --- a/src/libsam3/libsam3.c +++ b/src/libsam3/libsam3.c @@ -771,25 +771,35 @@ int sam3GenerateKeys(Sam3Session *ses, const char *hostname, int port, return -1; } // + if (sigType < 0 || sigType >= (int)(sizeof(sigtypes) / sizeof(sigtypes[0]))) { + strcpyerr(ses, "INVALID_SIGTYPE"); + sam3tcpDisconnect(fd); + return -1; + } if (sam3tcpPrintf(fd, "DEST GENERATE %s\n", sigtypes[(int)sigType]) < 0) { strcpyerr(ses, "DEST_ERROR"); + sam3tcpDisconnect(fd); + return -1; } rep = sam3ReadReply(fd); // sam3DumpFieldList(rep); - if (!sam3IsGoodReply(rep, "DEST", "REPLY", "PUB", NULL)) { + const char *pub = sam3FindField(rep, "PUB"); + const char *priv = sam3FindField(rep, "PRIV"); + if (!sam3IsGoodReply(rep, "DEST", "REPLY", NULL, NULL)) { + strcpyerr(ses, "DEST_ERROR"); + } else if (pub == NULL || !sam3CheckValidKeyLength(pub)) { strcpyerr(ses, "PUBKEY_ERROR"); - } - if (!sam3IsGoodReply(rep, "DEST", "REPLY", "PRIV", NULL)) { + } else if (priv == NULL || strlen(priv) < SAM3_PRIVKEY_MIN_SIZE || + strlen(priv) > SAM3_PRIVKEY_MAX_SIZE) { strcpyerr(ses, "PRIVKEY_ERROR"); + } else { + strncpy(ses->pubkey, pub, sizeof(ses->pubkey) - 1); + ses->pubkey[sizeof(ses->pubkey) - 1] = 0; + strncpy(ses->privkey, priv, sizeof(ses->privkey) - 1); + ses->privkey[sizeof(ses->privkey) - 1] = 0; + res = 0; } - const char *pub = sam3FindField(rep, "PUB"); - strncpy(ses->pubkey, pub, sizeof(ses->pubkey) - 1); - ses->pubkey[sizeof(ses->pubkey) - 1] = 0; - const char *priv = sam3FindField(rep, "PRIV"); - strncpy(ses->privkey, priv, sizeof(ses->privkey) - 1); - ses->privkey[sizeof(ses->privkey) - 1] = 0; - res = 0; // sam3FreeFieldList(rep); sam3tcpDisconnect(fd);