source: trunk/package/ead/src/ead-client.c @ 13828

Last change on this file since 13828 was 13828, checked in by nbd, 8 years ago

ead: message handling fixes

File size: 8.3 KB
Line 
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
40static char msgbuf[1500];
41static struct ead_msg *msg = (struct ead_msg *) msgbuf;
42static uint16_t nid = 0xffff;
43struct sockaddr_in local, remote;
44static int s = 0;
45static int sockflags;
46
47static unsigned char *skey = NULL;
48static unsigned char bbuf[MAXPARAMLEN];
49static unsigned char saltbuf[MAXSALTLEN];
50static char *username = NULL;
51static char password[MAXPARAMLEN] = "";
52static char pw_md5[MD5_OUT_BUFSIZE];
53static char pw_salt[MAXSALTLEN];
54
55static struct t_client *tc = NULL;
56static struct t_num salt = { .data = saltbuf };
57static struct t_num *A, B;
58static struct t_preconf *tcp;
59static int auth_type = EAD_AUTH_DEFAULT;
60static int timeout = EAD_TIMEOUT;
61
62static void
63set_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
72static int
73send_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
129static void
130prepare_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
142static bool
143handle_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
158static bool
159handle_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
181static bool
182handle_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
193static bool
194handle_none(void)
195{
196        return true;
197}
198
199static bool
200handle_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
210static bool
211handle_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}
225static int
226send_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
233static int
234send_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
242static int
243get_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
250static int
251send_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
261static int
262send_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
279static int
280send_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
293static int
294usage(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
307int 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}
Note: See TracBrowser for help on using the repository browser.