/* * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Hyra nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OSTP_PORT 5352 #if DIAGNOSTIC #define LOG(...) printf("libostp: %s", __VA_ARGS__) #else #define LOG(...) (void)0 #endif static const char *pap_codestr[] = { [PAP_SUCCESS] = "success", [PAP_BAD_SPW] = "bad SPW", [PAP_BAD_PERMS] = "insufficient permissions", [PAP_RESOURCE] = "server out of resources" }; static const char *auth_codestr[] = { [AUTH_SUCCESS] = "successful auth", [AUTH_BAD_PW] = "bad username or password" }; static int send_auth(int sockfd, const unsigned char *session_key) { struct session_auth auth; struct termios oldt, newt; int c, index = 0; int error; printf("** Please authenticate yourself **\n"); printf("Username: "); /* Fetch username and clean it up */ fgets(auth.username, sizeof(auth.username), stdin); auth.username[strcspn(auth.username, "\n")] = '\0'; /* Hide input */ tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ECHO | ICANON); tcsetattr(STDIN_FILENO, TCSANOW, &newt); printf("Password: "); while (index < (sizeof(auth.password) - 1)) { c = getchar(); if (c == '\n') { printf("\n"); break; } if (c == 127 || c == '\b') { if (index > 0) { auth.password[index] = '\0'; index--; printf("\b \b"); } } else { auth.password[index++] = c; printf("*"); } } auth.code = 0; auth.password[index] = '\0'; tcsetattr(STDIN_FILENO, TCSANOW, &oldt); /* Send frame with session auth */ error = send_frame(sockfd, &auth, sizeof(auth), session_key); if (error < 0) { printf("Failed to send user authentication\n"); return error; } /* Try to get server reply */ error = recv_frame(sockfd, sizeof(auth), session_key, &auth); if (error < 0) { LOG("Failed to get user authentication ACK from server\n"); return error; } /* Did it fail? */ if (auth.code != AUTH_SUCCESS) { LOG("Failed to authenticate, server says: %s\n", auth_codestr[auth.code]); return -1; } return 0; } static int client_negotiate_spw(int sockfd, const unsigned char *session_key) { struct aes_message am; struct msg_frame msg_frame; struct pap pap; unsigned char *tmp; int error; /* Create a PAP and encrypt it */ pap.spw = 0x8000; pap.code = 0; while (1) { if ((error = send_frame(sockfd, &pap, sizeof(pap), session_key)) < 0) { return error; } /* Receive the PAP the server replies with */ if ((error = recv_frame(sockfd, sizeof(pap), session_key, &pap)) < 0) { return error; } /* * If the server echos our PAP with code 0 then it has * applied our session parameters! However, there is * a chance arbitration will be needed and we need * to look out for that too... */ if (pap.code == 0) { LOG("** Negotiation success\n"); break; } LOG("** Arbitration needed, server says: %s\n", pap_codestr[pap.code]); LOG("** Server proposes SPW of 0x%0X, accepting\n", pap.spw); pap.spw |= PAP_SPW_QSR; pap.code = 0; } return 0; } static void recv_motd(int sockfd, const unsigned char *session_key) { char buf[4096]; int len; /* Receive the PAP the server replies with */ if ((len = recv_frame(sockfd, sizeof(buf) - 1, session_key, buf)) < 0) { printf("Failed to recv MOTD...\n"); return; } buf[len] = '\0'; printf("%s\n", buf); } int session_new(const char *host, struct ostp_session *res) { struct sockaddr_in addr; struct session_request stp_sq; struct x25519_keypair keypair; unsigned char serv_pubkey[32]; unsigned char *session_key; int error, sockfd; res->session_key = NULL; addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(host); addr.sin_port = htons(OSTP_PORT); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { return sockfd; } error = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); if (error < 0) { LOG("Failed to connect!\n"); close(sockfd); return error; } LOG("Generating keys...\n"); /* Generate an ephemeral keypair */ if ((error = gen_x25519_keypair(&keypair)) < 0) { LOG("Key generation failed!\n"); close(sockfd); return error; } /* * Setup the session request and add our public * key to it. We also assume user authentication. */ stp_sq.options = SESSION_REQ_USER; memcpy(stp_sq.pubkey, keypair.pubkey, sizeof(stp_sq.pubkey)); LOG("Sending session request...\n"); error = send(sockfd, &stp_sq, sizeof(stp_sq), 0); if (error < 0) { perror("Failed to send session request"); close(sockfd); return error; } error = recv(sockfd, serv_pubkey, sizeof(serv_pubkey), 0); if (error < 0) { perror("Failed to get public key from peer\n"); close(sockfd); return error; } if (error == 0) { LOG("Connection closed by peer\n"); close(sockfd); return 0; } LOG("Got public key from server\n"); LOG("Deriving session key...\n"); gen_session_key(keypair.privkey, serv_pubkey, &session_key); /* User authentication occurs before sending SPWs */ if ((error = send_auth(sockfd, session_key)) < 0) { return error; } /* Send server SPW bits */ if ((error = client_negotiate_spw(sockfd, session_key)) < 0) { LOG("Session Parameter Negotiation failed\n"); free_session_key(session_key); close(sockfd); return error; } recv_motd(sockfd, session_key); res->sockfd = sockfd; res->session_key = session_key; return 0; } int session_close(struct ostp_session *session) { if (session->session_key == NULL) { free_session_key(session->session_key); } close(session->sockfd); return 0; } int session_send(void *data, uint16_t len, const struct ostp_session *session) { return send_frame(session->sockfd, data, len, session->session_key); } int session_recv(void *buf, uint16_t len, const struct ostp_session *session) { return recv_frame(session->sockfd, len, session->session_key, buf); }