14e04c40ba94e36af134be57b715fd1e1438f123
[openwrt.git] / package / ead / src / ead-client.c
1 /*
2  * Client for the Emergency Access Daemon
3  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <sys/time.h>
18 #include <netinet/in.h>
19 #include <stdio.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <stdbool.h>
24 #include <string.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <t_pwd.h>
28 #include <t_read.h>
29 #include <t_sha.h>
30 #include <t_defines.h>
31 #include <t_client.h>
32 #include "ead.h"
33 #include "ead-crypt.h"
34
35 #include "pw_encrypt_md5.c"
36
37 #define EAD_TIMEOUT     400
38 #define EAD_TIMEOUT_LONG 2000
39
40 static char msgbuf[1500];
41 static struct ead_msg *msg = (struct ead_msg *) msgbuf;
42 static uint16_t nid = 0xffff;
43 struct sockaddr_in local, remote;
44 static int s = 0;
45 static int sockflags;
46
47 static unsigned char *skey = NULL;
48 static unsigned char bbuf[MAXPARAMLEN];
49 static unsigned char saltbuf[MAXSALTLEN];
50 static char *username = NULL;
51 static char password[MAXPARAMLEN] = "";
52 static char pw_md5[MD5_OUT_BUFSIZE];
53 static char pw_salt[MAXSALTLEN];
54
55 static struct t_client *tc = NULL;
56 static struct t_num salt = { .data = saltbuf };
57 static struct t_num *A, B;
58 static struct t_preconf *tcp;
59 static int auth_type = EAD_AUTH_DEFAULT;
60 static int timeout = EAD_TIMEOUT;
61
62 static void
63 set_nonblock(int enable)
64 {
65         if (enable == !!(sockflags & O_NONBLOCK));
66                 return;
67
68         sockflags ^= O_NONBLOCK;
69         fcntl(s, F_SETFL, sockflags);
70 }
71
72 static int
73 send_packet(int type, bool (*handler)(void), unsigned int max)
74 {
75         struct timeval tv;
76         fd_set fds;
77         int nfds;
78         int len;
79         int res = 0;
80
81         type = htonl(type);
82         set_nonblock(0);
83         sendto(s, msgbuf, sizeof(struct ead_msg) + ntohl(msg->len), 0, (struct sockaddr *) &remote, sizeof(remote));
84         set_nonblock(1);
85
86         tv.tv_sec = timeout / 1000;
87         tv.tv_usec = (timeout % 1000) * 1000;
88
89         FD_ZERO(&fds);
90         do {
91                 FD_SET(s, &fds);
92                 nfds = select(s + 1, &fds, NULL, NULL, &tv);
93
94                 if (nfds <= 0)
95                         break;
96
97                 if (!FD_ISSET(s, &fds))
98                         break;
99
100                 len = read(s, msgbuf, sizeof(msgbuf));
101                 if (len < 0)
102                         break;
103
104                 if (len < sizeof(struct ead_msg))
105                         continue;
106
107                 if (len < sizeof(struct ead_msg) + ntohl(msg->len))
108                         continue;
109
110                 if (msg->magic != htonl(EAD_MAGIC))
111                         continue;
112
113                 if ((nid != 0xffff) && (ntohs(msg->nid) != nid))
114                         continue;
115
116                 if (msg->type != type)
117                         continue;
118
119                 if (handler())
120                         res++;
121
122                 if ((max > 0) && (res >= max))
123                         break;
124         } while (1);
125
126         return res;
127 }
128
129 static void
130 prepare_password(void)
131 {
132         switch(auth_type) {
133         case EAD_AUTH_DEFAULT:
134                 break;
135         case EAD_AUTH_MD5:
136                 md5_crypt(pw_md5, (unsigned char *) password, (unsigned char *) pw_salt);
137                 strncpy(password, pw_md5, sizeof(password));
138                 break;
139         }
140 }
141
142 static bool
143 handle_pong(void)
144 {
145         struct ead_msg_pong *pong = EAD_DATA(msg, pong);
146         int len = ntohl(msg->len) - sizeof(struct ead_msg_pong);
147
148         if (len <= 0)
149                 return false;
150
151         pong->name[len] = 0;
152         auth_type = ntohs(pong->auth_type);
153         if (nid == 0xffff)
154                 printf("%04x: %s\n", ntohs(msg->nid), pong->name);
155         return true;
156 }
157
158 static bool
159 handle_prime(void)
160 {
161         struct ead_msg_salt *sb = EAD_DATA(msg, salt);
162
163         salt.len = sb->len;
164         memcpy(salt.data, sb->salt, salt.len);
165
166         if (auth_type == EAD_AUTH_MD5) {
167                 memcpy(pw_salt, sb->ext_salt, MAXSALTLEN);
168                 pw_salt[MAXSALTLEN - 1] = 0;
169         }
170
171         tcp = t_getpreparam(sb->prime);
172         tc = t_clientopen(username, &tcp->modulus, &tcp->generator, &salt);
173         if (!tc) {
174                 fprintf(stderr, "Client open failed\n");
175                 return false;
176         }
177
178         return true;
179 }
180
181 static bool
182 handle_b(void)
183 {
184         struct ead_msg_number *num = EAD_DATA(msg, number);
185         int len = ntohl(msg->len) - sizeof(struct ead_msg_number);
186
187         B.data = bbuf;
188         B.len = len;
189         memcpy(bbuf, num->data, len);
190         return true;
191 }
192
193 static bool
194 handle_none(void)
195 {
196         return true;
197 }
198
199 static bool
200 handle_done_auth(void)
201 {
202         struct ead_msg_auth *auth = EAD_DATA(msg, auth);
203         if (t_clientverify(tc, auth->data) != 0) {
204                 fprintf(stderr, "Client auth verify failed\n");
205                 return false;
206         }
207         return true;
208 }
209
210 static bool
211 handle_cmd_data(void)
212 {
213         struct ead_msg_cmd_data *cmd = EAD_ENC_DATA(msg, cmd_data);
214         int datalen = ead_decrypt_message(msg) - sizeof(struct ead_msg_cmd_data);
215
216         if (datalen < 0)
217                 return false;
218
219         if (datalen > 0) {
220                 write(1, cmd->data, datalen);
221         }
222
223         return !!cmd->done;
224 }
225 static int
226 send_ping(void)
227 {
228         msg->type = htonl(EAD_TYPE_PING);
229         msg->len = 0;
230         return send_packet(EAD_TYPE_PONG, handle_pong, (nid == 0xffff ? 0 : 1));
231 }
232
233 static int
234 send_username(void)
235 {
236         msg->type = htonl(EAD_TYPE_SET_USERNAME);
237         msg->len = htonl(sizeof(struct ead_msg_user));
238         strcpy(EAD_DATA(msg, user)->username, username);
239         return send_packet(EAD_TYPE_ACK_USERNAME, handle_none, 1);
240 }
241
242 static int
243 get_prime(void)
244 {
245         msg->type = htonl(EAD_TYPE_GET_PRIME);
246         msg->len = 0;
247         return send_packet(EAD_TYPE_PRIME, handle_prime, 1);
248 }
249
250 static int
251 send_a(void)
252 {
253         struct ead_msg_number *num = EAD_DATA(msg, number);
254         A = t_clientgenexp(tc);
255         msg->type = htonl(EAD_TYPE_SEND_A);
256         msg->len = htonl(sizeof(struct ead_msg_number) + A->len);
257         memcpy(num->data, A->data, A->len);
258         return send_packet(EAD_TYPE_SEND_B, handle_b, 1);
259 }
260
261 static int
262 send_auth(void)
263 {
264         struct ead_msg_auth *auth = EAD_DATA(msg, auth);
265
266         prepare_password();
267         t_clientpasswd(tc, password);
268         skey = t_clientgetkey(tc, &B);
269         if (!skey)
270                 return 0;
271
272         ead_set_key(skey);
273         msg->type = htonl(EAD_TYPE_SEND_AUTH);
274         msg->len = htonl(sizeof(struct ead_msg_auth));
275         memcpy(auth->data, t_clientresponse(tc), sizeof(auth->data));
276         return send_packet(EAD_TYPE_DONE_AUTH, handle_done_auth, 1);
277 }
278
279 static int
280 send_command(const char *command)
281 {
282         struct ead_msg_cmd *cmd = EAD_ENC_DATA(msg, cmd);
283
284         msg->type = htonl(EAD_TYPE_SEND_CMD);
285         cmd->type = htons(EAD_CMD_NORMAL);
286         cmd->timeout = htons(10);
287         strncpy((char *)cmd->data, command, 1024);
288         ead_encrypt_message(msg, sizeof(struct ead_msg_cmd) + strlen(command) + 1);
289         return send_packet(EAD_TYPE_RESULT_CMD, handle_cmd_data, 1);
290 }
291
292
293 static int
294 usage(const char *prog)
295 {
296         fprintf(stderr, "Usage: %s <node> <username>[:<password>]\n"
297                 "\n"
298                 "\n<node>:     Node ID (4 digits hex)\n"
299                 "\n<username>: Username to authenticate with\n"
300                 "\n"
301                 "\nPassing no arguments shows a list of active nodes on the network\n"
302                 "\n", prog);
303         return -1;
304 }
305
306
307 int main(int argc, char **argv)
308 {
309         int val = 1;
310         char *st = NULL;
311         const char *command = NULL;
312
313         msg->magic = htonl(EAD_MAGIC);
314         msg->tid = 0;
315
316         memset(&local, 0, sizeof(local));
317         memset(&remote, 0, sizeof(remote));
318
319         remote.sin_family = AF_INET;
320         remote.sin_addr.s_addr = 0xffffffff;
321         remote.sin_port = htons(EAD_PORT);
322
323         local.sin_family = AF_INET;
324         local.sin_addr.s_addr = INADDR_ANY;
325         local.sin_port = 0;
326
327         switch(argc) {
328         case 4:
329                 command = argv[3];
330                 /* fall through */
331         case 3:
332                 username = argv[2];
333                 st = strchr(username, ':');
334                 if (st) {
335                         *st = 0;
336                         st++;
337                         strncpy(password, st, sizeof(password));
338                         password[sizeof(password) - 1] = 0;
339                         /* hide command line password */
340                         memset(st, 0, strlen(st));
341                 }
342                 /* fall through */
343         case 2:
344                 nid = strtoul(argv[1], &st, 16);
345                 if (st && st[0] != 0)
346                         return usage(argv[0]);
347                 /* fall through */
348         case 1:
349                 break;
350         default:
351                 return usage(argv[0]);
352         }
353
354         msg->nid = htons(nid);
355         s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
356         if (s < 0) {
357                 perror("socket");
358                 return -1;
359         }
360
361         setsockopt(s, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val));
362
363         if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
364                 perror("bind");
365                 return -1;
366         }
367         sockflags = fcntl(s, F_GETFL);
368
369         if (!send_ping()) {
370                 fprintf(stderr, "No devices found\n");
371                 return 1;
372         }
373
374         if (nid == 0xffff)
375                 return 0;
376
377         if (!username || !password[0])
378                 return 0;
379
380         if (!send_username()) {
381                 fprintf(stderr, "Device did not accept user name\n");
382                 return 1;
383         }
384         if (!get_prime()) {
385                 fprintf(stderr, "Failed to get user password info\n");
386                 return 1;
387         }
388
389         timeout = EAD_TIMEOUT_LONG;
390         if (!send_a()) {
391                 fprintf(stderr, "Failed to send local authentication data\n");
392                 return 1;
393         }
394         if (!send_auth()) {
395                 fprintf(stderr, "Authentication failed\n");
396                 return 1;
397         }
398         if (!command) {
399                 fprintf(stderr, "Authentication succesful\n");
400                 return 0;
401         }
402         if (!send_command(command)) {
403                 fprintf(stderr, "Command failed\n");
404                 return 1;
405         }
406
407         return 0;
408 }