add the 'ead' package (emergency access daemon), which can provide remote access...
[openwrt/openwrt.git] / package / ead / src / ead-client.c
diff --git a/package/ead/src/ead-client.c b/package/ead/src/ead-client.c
new file mode 100644 (file)
index 0000000..111dc8a
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * Client for the Emergency Access Daemon
+ * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <t_pwd.h>
+#include <t_read.h>
+#include <t_sha.h>
+#include <t_defines.h>
+#include <t_client.h>
+#include "ead.h"
+#include "ead-crypt.h"
+
+#include "pw_encrypt_md5.c"
+
+#define EAD_TIMEOUT    400
+#define EAD_TIMEOUT_LONG 2000
+
+static char msgbuf[1500];
+static struct ead_msg *msg = (struct ead_msg *) msgbuf;
+static uint16_t nid = 0xffff;
+struct sockaddr_in local, remote;
+static int s = 0;
+static int sockflags;
+
+static unsigned char *skey = NULL;
+static unsigned char bbuf[MAXPARAMLEN];
+static unsigned char saltbuf[MAXSALTLEN];
+static char *username = NULL;
+static char password[MAXPARAMLEN] = "";
+static char pw_md5[MD5_OUT_BUFSIZE];
+static char pw_salt[MAXSALTLEN];
+
+static struct t_client *tc = NULL;
+static struct t_num salt = { .data = saltbuf };
+static struct t_num *A, B;
+static struct t_preconf *tcp;
+static int auth_type = EAD_AUTH_DEFAULT;
+static int timeout = EAD_TIMEOUT;
+
+static void
+set_nonblock(int enable)
+{
+       if (enable == !!(sockflags & O_NONBLOCK));
+               return;
+
+       sockflags ^= O_NONBLOCK;
+       fcntl(s, F_SETFL, sockflags);
+}
+
+static int
+send_packet(int type, bool (*handler)(void), unsigned int max)
+{
+       struct timeval tv;
+       fd_set fds;
+       int nfds;
+       int len;
+       int res = 0;
+
+       type = htonl(type);
+       set_nonblock(0);
+       sendto(s, msgbuf, sizeof(struct ead_msg) + ntohl(msg->len), 0, (struct sockaddr *) &remote, sizeof(remote));
+       set_nonblock(1);
+
+       tv.tv_sec = timeout / 1000;
+       tv.tv_usec = (timeout % 1000) * 1000;
+
+       FD_ZERO(&fds);
+       do {
+               FD_SET(s, &fds);
+               nfds = select(s + 1, &fds, NULL, NULL, &tv);
+
+               if (nfds <= 0)
+                       break;
+
+               if (!FD_ISSET(s, &fds))
+                       break;
+
+               len = read(s, msgbuf, sizeof(msgbuf));
+               if (len < 0)
+                       break;
+
+               if (len < sizeof(struct ead_msg))
+                       continue;
+
+               if (len < sizeof(struct ead_msg) + ntohl(msg->len))
+                       continue;
+
+               if (msg->magic != htonl(EAD_MAGIC))
+                       continue;
+
+               if ((nid != 0xffff) && (ntohs(msg->nid) != nid))
+                       continue;
+
+               if (msg->type != type)
+                       continue;
+
+               if (handler())
+                       res++;
+
+               if ((max > 0) && (res >= max))
+                       break;
+       } while (1);
+
+       return res;
+}
+
+static void
+prepare_password(void)
+{
+       switch(auth_type) {
+       case EAD_AUTH_DEFAULT:
+               break;
+       case EAD_AUTH_MD5:
+               md5_crypt(pw_md5, (unsigned char *) password, (unsigned char *) pw_salt);
+               strncpy(password, pw_md5, sizeof(password));
+               break;
+       }
+}
+
+static bool
+handle_pong(void)
+{
+       struct ead_msg_pong *pong = EAD_DATA(msg, pong);
+       int len = msg->len - sizeof(struct ead_msg_pong);
+
+       pong->name[len] = 0;
+       auth_type = ntohs(pong->auth_type);
+       if (nid == 0xffff)
+               printf("%04x: %s\n", ntohs(msg->nid), pong->name);
+       return true;
+}
+
+static bool
+handle_prime(void)
+{
+       struct ead_msg_salt *sb = EAD_DATA(msg, salt);
+
+       salt.len = sb->len;
+       memcpy(salt.data, sb->salt, salt.len);
+
+       if (auth_type == EAD_AUTH_MD5) {
+               memcpy(pw_salt, sb->ext_salt, MAXSALTLEN);
+               pw_salt[MAXSALTLEN - 1] = 0;
+       }
+
+       tcp = t_getpreparam(sb->prime);
+       tc = t_clientopen(username, &tcp->modulus, &tcp->generator, &salt);
+       if (!tc) {
+               fprintf(stderr, "Client open failed\n");
+               return false;
+       }
+
+       return true;
+}
+
+static bool
+handle_b(void)
+{
+       struct ead_msg_number *num = EAD_DATA(msg, number);
+       int len = ntohl(msg->len) - sizeof(struct ead_msg_number);
+
+       B.data = bbuf;
+       B.len = len;
+       memcpy(bbuf, num->data, len);
+       return true;
+}
+
+static bool
+handle_none(void)
+{
+       return true;
+}
+
+static bool
+handle_done_auth(void)
+{
+       struct ead_msg_auth *auth = EAD_DATA(msg, auth);
+       if (t_clientverify(tc, auth->data) != 0) {
+               fprintf(stderr, "Client auth verify failed\n");
+               return false;
+       }
+       return true;
+}
+
+static bool
+handle_cmd_data(void)
+{
+       struct ead_msg_cmd_data *cmd = EAD_ENC_DATA(msg, cmd_data);
+       int datalen = ead_decrypt_message(msg) - sizeof(struct ead_msg_cmd_data);
+
+       if (datalen < 0)
+               return false;
+
+       if (datalen > 0) {
+               write(1, cmd->data, datalen);
+       }
+
+       return !!cmd->done;
+}
+static int
+send_ping(void)
+{
+       msg->type = htonl(EAD_TYPE_PING);
+       msg->len = 0;
+       return send_packet(EAD_TYPE_PONG, handle_pong, (nid == 0xffff ? 0 : 1));
+}
+
+static int
+send_username(void)
+{
+       msg->type = htonl(EAD_TYPE_SET_USERNAME);
+       msg->len = htonl(sizeof(struct ead_msg_user));
+       strcpy(EAD_DATA(msg, user)->username, username);
+       return send_packet(EAD_TYPE_ACK_USERNAME, handle_none, 1);
+}
+
+static int
+get_prime(void)
+{
+       msg->type = htonl(EAD_TYPE_GET_PRIME);
+       msg->len = 0;
+       return send_packet(EAD_TYPE_PRIME, handle_prime, 1);
+}
+
+static int
+send_a(void)
+{
+       struct ead_msg_number *num = EAD_DATA(msg, number);
+       A = t_clientgenexp(tc);
+       msg->type = htonl(EAD_TYPE_SEND_A);
+       msg->len = htonl(sizeof(struct ead_msg_number) + A->len);
+       memcpy(num->data, A->data, A->len);
+       return send_packet(EAD_TYPE_SEND_B, handle_b, 1);
+}
+
+static int
+send_auth(void)
+{
+       struct ead_msg_auth *auth = EAD_DATA(msg, auth);
+
+       prepare_password();
+       t_clientpasswd(tc, password);
+       skey = t_clientgetkey(tc, &B);
+       if (!skey)
+               return 0;
+
+       ead_set_key(skey);
+       msg->type = htonl(EAD_TYPE_SEND_AUTH);
+       msg->len = htonl(sizeof(struct ead_msg_auth));
+       memcpy(auth->data, t_clientresponse(tc), sizeof(auth->data));
+       return send_packet(EAD_TYPE_DONE_AUTH, handle_done_auth, 1);
+}
+
+static int
+send_command(const char *command)
+{
+       struct ead_msg_cmd *cmd = EAD_ENC_DATA(msg, cmd);
+
+       msg->type = htonl(EAD_TYPE_SEND_CMD);
+       cmd->type = htons(EAD_CMD_NORMAL);
+       cmd->timeout = htons(10);
+       strncpy((char *)cmd->data, command, 1024);
+       ead_encrypt_message(msg, sizeof(struct ead_msg_cmd) + strlen(command) + 1);
+       return send_packet(EAD_TYPE_RESULT_CMD, handle_cmd_data, 1);
+}
+
+
+static int
+usage(const char *prog)
+{
+       fprintf(stderr, "Usage: %s <node> <username>[:<password>]\n"
+               "\n"
+               "\n<node>:     Node ID (4 digits hex)\n"
+               "\n<username>: Username to authenticate with\n"
+               "\n"
+               "\nPassing no arguments shows a list of active nodes on the network\n"
+               "\n", prog);
+       return -1;
+}
+
+
+int main(int argc, char **argv)
+{
+       int val = 1;
+       char *st = NULL;
+       const char *command = NULL;
+
+       msg->magic = htonl(EAD_MAGIC);
+       msg->tid = 0;
+
+       memset(&local, 0, sizeof(local));
+       memset(&remote, 0, sizeof(remote));
+
+       remote.sin_family = AF_INET;
+       remote.sin_addr.s_addr = 0xffffffff;
+       remote.sin_port = htons(EAD_PORT);
+
+       local.sin_family = AF_INET;
+       local.sin_addr.s_addr = INADDR_ANY;
+       local.sin_port = 0;
+
+       switch(argc) {
+       case 4:
+               command = argv[3];
+               /* fall through */
+       case 3:
+               username = argv[2];
+               st = strchr(username, ':');
+               if (st) {
+                       *st = 0;
+                       st++;
+                       strncpy(password, st, sizeof(password));
+                       password[sizeof(password) - 1] = 0;
+                       /* hide command line password */
+                       memset(st, 0, strlen(st));
+               }
+               /* fall through */
+       case 2:
+               nid = strtoul(argv[1], &st, 16);
+               if (st && st[0] != 0)
+                       return usage(argv[0]);
+               /* fall through */
+       case 1:
+               break;
+       default:
+               return usage(argv[0]);
+       }
+
+       msg->nid = htons(nid);
+       s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       if (s < 0) {
+               perror("socket");
+               return -1;
+       }
+
+       setsockopt(s, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val));
+
+       if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
+               perror("bind");
+               return -1;
+       }
+       sockflags = fcntl(s, F_GETFL);
+
+       if (!send_ping()) {
+               fprintf(stderr, "No devices found\n");
+               return 1;
+       }
+
+       if (nid == 0xffff)
+               return 0;
+
+       if (!username || !password[0])
+               return 0;
+
+       if (!send_username()) {
+               fprintf(stderr, "Device did not accept user name\n");
+               return 1;
+       }
+       if (!get_prime()) {
+               fprintf(stderr, "Failed to get user password info\n");
+               return 1;
+       }
+
+       timeout = EAD_TIMEOUT_LONG;
+       if (!send_a()) {
+               fprintf(stderr, "Failed to send local authentication data\n");
+               return 1;
+       }
+       if (!send_auth()) {
+               fprintf(stderr, "Authentication failed\n");
+               return 1;
+       }
+       if (!command) {
+               fprintf(stderr, "Authentication succesful\n");
+               return 0;
+       }
+       if (!send_command(command)) {
+               fprintf(stderr, "Command failed\n");
+               return 1;
+       }
+
+       return 0;
+}