aboutsummaryrefslogtreecommitdiff
path: root/lib/libostp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libostp')
-rw-r--r--lib/libostp/auth.c199
-rw-r--r--lib/libostp/param.c96
-rw-r--r--lib/libostp/server.c195
-rw-r--r--lib/libostp/session.c299
4 files changed, 789 insertions, 0 deletions
diff --git a/lib/libostp/auth.c b/lib/libostp/auth.c
new file mode 100644
index 0000000..d32c06a
--- /dev/null
+++ b/lib/libostp/auth.c
@@ -0,0 +1,199 @@
+/*
+ * 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 <sys/wait.h>
+#include <net/auth.h>
+#include <crypto/ecdh.h>
+#include <otconfig.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/*
+ * Check a password to see if it matches with
+ * the hash in /etc/shadow by using the pwcheck
+ * script. Returns 0 on success.
+ */
+static int
+pwcheck(char *username, char *pw)
+{
+ char *pwcheck = "/usr/local/bin/pwcheck";
+ pid_t pid;
+ char *args[] = {pwcheck, username, pw, NULL};
+ int status;
+
+ pid = fork();
+ if (pid == 0) {
+ execv(pwcheck, args);
+ }
+
+ if (waitpid(pid, &status, 0) < 0) {
+ printf("waidpid() failed\n");
+ return -1;
+ }
+
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ }
+
+ return -1;
+}
+
+static int
+passwd_auth(struct ostp_session *sp, const unsigned char *session_key)
+{
+ int error;
+ struct session_auth auth;
+ const size_t LEN = sizeof(auth);
+
+ if (!REQUIRE_USER_AUTH) {
+ return 0;
+ }
+
+ error = recv_frame(sp->sockfd, sizeof(auth), session_key, &auth);
+ if (error < 0) {
+ return error;
+ }
+
+ if (pwcheck(auth.username, auth.password) != 0) {
+ printf("Got bad password for %s\n", auth.username);
+ auth.code = AUTH_BAD_PW;
+ error = send_frame(sp->sockfd, &auth, sizeof(auth), session_key);
+ if (error < 0) {
+ printf("Failed to ACK user authentication with frame\n");
+ }
+ return -1;
+ }
+
+ auth.code = AUTH_SUCCESS;
+ error = send_frame(sp->sockfd, &auth, sizeof(auth), session_key);
+ if (error < 0) {
+ printf("Failed to ACK user authentication with frame\n");
+ return error;
+ }
+ return 0;
+}
+
+static void
+send_motd(struct ostp_session *sp, const unsigned char *session_key)
+{
+ char motd[] = MOTD;
+
+ printf("Sending MOTD...\n");
+ if (send_frame(sp->sockfd, motd, sizeof(motd), session_key) < 0) {
+ printf("Failed to session MOTD\n");
+ }
+}
+
+static int
+session_run(struct ostp_session *sp, struct ostp_listener *lp,
+ const unsigned char *session_key)
+{
+ char buf[4096];
+ size_t len;
+
+ while (1) {
+ for (int i = 1; i < MAX_CLIENTS; ++i) {
+ if (lp->clients[i] <= 0)
+ continue;
+ if (FD_ISSET(lp->clients[i], &lp->client_fds) <= 0)
+ continue;
+
+ len = recv_frame(lp->clients[i], sizeof(buf) - 1, session_key, buf);
+ if (len < 0) {
+ printf("recv_frame() failure, packet lost\n");
+ continue;
+ }
+ if (len == 0) {
+ return 0;
+ }
+ if (lp->on_recv != NULL) {
+ lp->on_recv(sp, buf, len);
+ }
+ }
+ }
+}
+
+int
+handle_srq(struct ostp_session *sp, struct ostp_listener *lp, struct session_request *srq)
+{
+ struct x25519_keypair keypair;
+ unsigned char *session_key;
+ pid_t child;
+ int error;
+
+ if (REQUIRE_USER_AUTH && !ISSET(srq->options, SESSION_REQ_USER)) {
+ printf("%x\n", srq->options);
+ printf("User authentication enforced but client 'U' bit not set\n");
+ printf("Closing connection...\n");
+ return -1;
+ }
+
+ printf("Generating keys...\n");
+
+ if (gen_x25519_keypair(&keypair) < 0) {
+ printf("Key generation failed!\n");
+ return -1;
+ }
+
+ /* Send back our our public key */
+ error = send(sp->sockfd, keypair.pubkey, keypair.pubkey_len, 0);
+ if (error < 0) {
+ perror("Failed to send public key");
+ return error;
+ }
+
+ printf("Deriving session key...\n");
+ error = gen_session_key(keypair.privkey, srq->pubkey, &session_key);
+ if (error < 0) {
+ return error;
+ }
+
+ /* Try user auth, not needed if REQUIRE_USER_AUTH is 0 */
+ if (passwd_auth(sp, session_key) != 0) {
+ return -1;
+ }
+
+ /* Handle any requested session parameters */
+ if ((error = negotiate_spw(sp, session_key)) < 0) {
+ free_session_key(session_key);
+ return error;
+ }
+
+ send_motd(sp, session_key);
+
+ /* Dispatch a thread and handle this session */
+ child = fork();
+ if (child == 0) {
+ session_run(sp, lp, session_key);
+ exit(0);
+ }
+ return 0;
+}
diff --git a/lib/libostp/param.c b/lib/libostp/param.c
new file mode 100644
index 0000000..4c14733
--- /dev/null
+++ b/lib/libostp/param.c
@@ -0,0 +1,96 @@
+/*
+ * 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 <net/stpsession.h>
+#include <libostp/session.h>
+#include <crypto/aes.h>
+#include <net/param.h>
+#include <net/auth.h>
+#include <otconfig.h>
+#include <stdio.h>
+
+static int
+handle_pap(struct ostp_session *sp, const struct pap *pap, const unsigned char *session_key)
+{
+ int error = -1;
+ uint8_t attempts = 0;
+ struct pap tmp_pap = *pap;
+ const size_t LEN = sizeof(struct pap);
+
+ /* TODO: Support more SPW bits */
+ while (1) {
+ /* Quick session request, jump right in! */
+ if (ISSET(tmp_pap.spw, PAP_SPW_QSR)) {
+ printf("Got QSR, starting session...\n");
+ send_frame(sp->sockfd, &tmp_pap, LEN, session_key);
+ return 0;
+ }
+
+ /* We have exhausted our attempts */
+ if (attempts >= ARBITRATION_MAX) {
+ printf("Too many arbitration attempts, bailing!\n");
+ return -1;
+ }
+
+ printf("Got bad SPW from client\n");
+ printf("Attempting arbitration...\n");
+
+ tmp_pap.spw = 0;
+ tmp_pap.code = PAP_BAD_SPW;
+
+ /* Send in PAP and wait for response */
+ if ((error = send_frame(sp->sockfd, &tmp_pap, LEN, session_key)) < -1) {
+ printf("Failed to send PAP frame\n");
+ return -1;
+ }
+ if ((error = recv_frame(sp->sockfd, LEN, session_key, &tmp_pap)) < -1) {
+ printf("Failed to recv PAP frame\n");
+ return error;
+ }
+
+ ++attempts;
+ }
+
+ return 0;
+}
+
+int
+negotiate_spw(struct ostp_session *sp, unsigned char *session_key)
+{
+ const size_t LEN = sizeof(struct pap);
+ struct pap pap;
+ int error;
+
+ /* Get PAP from the network */
+ if ((error = recv_frame(sp->sockfd, LEN, session_key, &pap)) < -1) {
+ return error;
+ }
+
+ return handle_pap(sp, &pap, session_key);
+}
diff --git a/lib/libostp/server.c b/lib/libostp/server.c
new file mode 100644
index 0000000..0013ce2
--- /dev/null
+++ b/lib/libostp/server.c
@@ -0,0 +1,195 @@
+/*
+ * 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 <libostp/server.h>
+#include <net/auth.h>
+#include <net/stpsession.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#define MAX_BACKLOG 4
+#define LISTEN_PORT 5352
+
+static int
+handle_client(struct sockaddr_in *caddr, struct ostp_session *sp, struct ostp_listener *lp,
+ int clientno)
+{
+ struct session_request srq;
+ ssize_t nread;
+
+ sp->sockfd = lp->clients[clientno];
+
+ /* Try to read in the session request */
+ if ((nread = read(sp->sockfd, &srq, sizeof(srq))) < 0) {
+ printf("Read failure...\n");
+ close(sp->sockfd);
+ lp->clients[clientno] = -1;
+ return -1;
+ }
+
+ if (nread == 0) {
+ printf("Connection closed by peer\n");
+ close(sp->sockfd);
+ lp->clients[clientno] = -1;
+ return -1;
+ }
+
+ /* Is this even a session request? */
+ if (nread != sizeof(srq)) {
+ printf("Rejecting data - not a session request...\n");
+ close(sp->sockfd);
+ lp->clients[clientno] = -1;
+ return -1;
+ }
+
+ /* Handle the session request */
+ if (handle_srq(sp, lp, &srq) < 0) {
+ close(sp->sockfd);
+ lp->clients[clientno] = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Put a listener in a known state during
+ * initialization.
+ */
+void
+listener_init(struct ostp_listener *lp)
+{
+ lp->port = LISTEN_PORT;
+ lp->on_recv = NULL;
+}
+
+int
+listener_bind(struct ostp_session *sp, struct ostp_listener *lp)
+{
+ struct sockaddr_in saddr;
+ int error;
+
+ lp->serv_sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sp->sockfd < 0) {
+ perror("Failed to create socket\n");
+ return -1;
+ }
+
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = INADDR_ANY;
+ saddr.sin_port = htons(LISTEN_PORT);
+ error = bind(lp->serv_sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (error < 0) {
+ perror("Failed to bind socket\n");
+ close(lp->serv_sockfd);
+ return error;
+ }
+
+ if ((error = listen(lp->serv_sockfd, MAX_BACKLOG)) < 0) {
+ perror("Failed to listen");
+ return error;
+ }
+
+ return 0;
+}
+
+int
+listener_poll(struct ostp_session *sp, struct ostp_listener *lp)
+{
+ struct sockaddr_in caddr;
+ socklen_t caddr_len;
+ int client_sock, error = 0;
+ char *ip;
+
+ memset(lp->clients, -1, sizeof(lp->clients));
+ lp->clients[0] = lp->serv_sockfd;
+
+ while (1) {
+ FD_ZERO(&lp->client_fds);
+
+ for (int i = 0; i < MAX_CLIENTS; ++i) {
+ if (lp->clients[i] >= 0)
+ FD_SET(lp->clients[i], &lp->client_fds);
+ }
+
+ if (select(1024, &lp->client_fds, NULL, NULL, NULL) < 0) {
+ perror("select");
+ continue;
+ }
+
+ /* Check if the servers socket has new connections */
+ if (FD_ISSET(lp->serv_sockfd, &lp->client_fds)) {
+ caddr_len = sizeof(caddr);
+ client_sock = accept(lp->serv_sockfd, (struct sockaddr *)&caddr,
+ &caddr_len);
+
+ if (client_sock < 0) {
+ perror("accept");
+ continue;
+ }
+
+ for (int i = 0; i < MAX_CLIENTS; ++i) {
+ if (lp->clients[i] < 0) {
+ lp->clients[i] = client_sock;
+ ip = inet_ntoa(caddr.sin_addr);
+ printf("Incoming connection from %s\n", ip);
+ break;
+ }
+ }
+ }
+
+ /* Handle from data from lp->clients */
+ for (int i = 1; i < MAX_CLIENTS; ++i) {
+ if (lp->clients[i] <= 0)
+ continue;
+ if (FD_ISSET(lp->clients[i], &lp->client_fds) <= 0)
+ continue;
+
+ handle_client(&caddr, sp, lp, i);
+ break;
+ }
+ }
+
+ close(client_sock);
+ close(lp->serv_sockfd);
+}
+
+void
+listener_cleanup(struct ostp_listener *lp)
+{
+ for (int i = 0; i < MAX_CLIENTS; ++i) {
+ if (lp->clients[i] > 0) {
+ close(lp->clients[i]);
+ }
+ }
+
+ close(lp->serv_sockfd);
+}
diff --git a/lib/libostp/session.c b/lib/libostp/session.c
new file mode 100644
index 0000000..4bc6fb4
--- /dev/null
+++ b/lib/libostp/session.c
@@ -0,0 +1,299 @@
+/*
+ * 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 <libostp/session.h>
+#include <net/stpsession.h>
+#include <net/param.h>
+#include <crypto/ecdh.h>
+#include <defs.h>
+#include <arpa/inet.h>
+#include <termios.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define OSTP_PORT 5352
+
+#if LIBOSTP_DEBUG
+#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.
+ */
+ memset(stp_sq.hash, 0, sizeof(stp_sq.hash));
+ 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);
+}