aboutsummaryrefslogtreecommitdiffstats
path: root/package/network/services/ead/src/ead-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'package/network/services/ead/src/ead-client.c')
-rw-r--r--package/network/services/ead/src/ead-client.c433
1 files changed, 433 insertions, 0 deletions
diff --git a/package/network/services/ead/src/ead-client.c b/package/network/services/ead/src/ead-client.c
new file mode 100644
index 0000000..6d7e07d
--- /dev/null
+++ b/package/network/services/ead/src/ead-client.c
@@ -0,0 +1,433 @@
+/*
+ * 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 <arpa/inet.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 struct in_addr serverip = {
+ .s_addr = 0x01010101 /* dummy */
+};
+
+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 uint16_t sid = 0;
+
+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);
+ memcpy(&msg->ip, &serverip.s_addr, sizeof(msg->ip));
+ 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 = ntohl(msg->len) - sizeof(struct ead_msg_pong);
+
+ if (len <= 0)
+ return false;
+
+ pong->name[len] = 0;
+ auth_type = ntohs(pong->auth_type);
+ if (nid == 0xffff)
+ printf("%04x: %s\n", ntohs(msg->nid), pong->name);
+ sid = msg->sid;
+ 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 [-s <addr>] [-b <addr>] <node> <username>[:<password>] <command>\n"
+ "\n"
+ "\t-s <addr>: Set the server's source address to <addr>\n"
+ "\t-b <addr>: Set the broadcast address to <addr>\n"
+ "\t<node>: Node ID (4 digits hex)\n"
+ "\t<username>: Username to authenticate with\n"
+ "\n"
+ "\tPassing 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;
+ const char *prog = argv[0];
+ int ch;
+
+ msg->magic = htonl(EAD_MAGIC);
+ msg->sid = 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;
+
+ while ((ch = getopt(argc, argv, "b:s:h")) != -1) {
+ switch(ch) {
+ case 's':
+ inet_aton(optarg, &serverip);
+ break;
+ case 'b':
+ inet_aton(optarg, &remote.sin_addr);
+ break;
+ case 'h':
+ return usage(prog);
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ switch(argc) {
+ case 3:
+ command = argv[2];
+ /* fall through */
+ case 2:
+ username = argv[1];
+ 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 1:
+ nid = strtoul(argv[0], &st, 16);
+ if (st && st[0] != 0)
+ return usage(prog);
+ /* fall through */
+ case 0:
+ break;
+ default:
+ return usage(prog);
+ }
+
+ 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;
+ }
+ timeout = EAD_TIMEOUT_LONG;
+ if (!get_prime()) {
+ fprintf(stderr, "Failed to get user password info\n");
+ return 1;
+ }
+ 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;
+}