busybox/networking/ssl_helper/ssl_helper.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2013 INSIDE Secure Corporation
   3 * Copyright (c) PeerSec Networks, 2002-2011
   4 * All Rights Reserved
   5 *
   6 * The latest version of this code is available at http://www.matrixssl.org
   7 *
   8 * This software is open source; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in WITHOUT ANY WARRANTY; without even the
  14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15 * See the GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 * http://www.gnu.org/copyleft/gpl.html
  21 */
  22#include <errno.h>
  23#include <stdlib.h>
  24#include <unistd.h>
  25#include <stdarg.h>
  26#include <fcntl.h>
  27#include <stdio.h>
  28#include <time.h>
  29#include <poll.h>
  30#include <sys/socket.h>
  31
  32#include "matrixssl/matrixsslApi.h"
  33
  34//#warning "DO NOT USE THESE DEFAULT KEYS IN PRODUCTION ENVIRONMENTS."
  35
  36/*
  37 * If supporting client authentication, pick ONE identity to auto select a
  38 * certificate and private key that support desired algorithms.
  39 */
  40#define ID_RSA /* RSA Certificate and Key */
  41
  42#define USE_HEADER_KEYS
  43
  44/* If the algorithm type is supported, load a CA for it */
  45#ifdef USE_HEADER_KEYS
  46/* CAs */
  47# include "sampleCerts/RSA/ALL_RSA_CAS.h"
  48/* Identity Certs and Keys for use with Client Authentication */
  49# ifdef ID_RSA
  50#  define EXAMPLE_RSA_KEYS
  51#  include "sampleCerts/RSA/2048_RSA.h"
  52#  include "sampleCerts/RSA/2048_RSA_KEY.h"
  53# endif
  54#endif
  55
  56static ssize_t safe_write(int fd, const void *buf, size_t count)
  57{
  58        ssize_t n;
  59
  60        do {
  61                n = write(fd, buf, count);
  62        } while (n < 0 && errno == EINTR);
  63
  64        return n;
  65}
  66
  67static ssize_t full_write(int fd, const void *buf, size_t len)
  68{
  69        ssize_t cc;
  70        ssize_t total;
  71
  72        total = 0;
  73
  74        while (len) {
  75                cc = safe_write(fd, buf, len);
  76
  77                if (cc < 0) {
  78                        if (total) {
  79                                /* we already wrote some! */
  80                                /* user can do another write to know the error code */
  81                                return total;
  82                        }
  83                        return cc;  /* write() returns -1 on failure. */
  84                }
  85
  86                total += cc;
  87                buf = ((const char *)buf) + cc;
  88                len -= cc;
  89        }
  90
  91        return total;
  92}
  93
  94static void say(const char *s, ...)
  95{
  96        char buf[256];
  97        va_list p;
  98        int sz;
  99
 100        va_start(p, s);
 101        sz = vsnprintf(buf, sizeof(buf), s, p);
 102        full_write(STDERR_FILENO, buf, sz >= 0 && sz < sizeof(buf) ? sz : strlen(buf));
 103        va_end(p);
 104}
 105
 106static void die(const char *s, ...)
 107{
 108        char buf[256];
 109        va_list p;
 110        int sz;
 111
 112        va_start(p, s);
 113        sz = vsnprintf(buf, sizeof(buf), s, p);
 114        full_write(STDERR_FILENO, buf, sz >= 0 && sz < sizeof(buf) ? sz : strlen(buf));
 115        exit(1);
 116        va_end(p);
 117}
 118
 119#if 0
 120# define dbg(...) say(__VA_ARGS__)
 121#else
 122# define dbg(...) ((void)0)
 123#endif
 124
 125static struct pollfd pfd[2] = {
 126        { -1, POLLIN|POLLERR|POLLHUP, 0 },
 127        { -1, POLLIN|POLLERR|POLLHUP, 0 },
 128};
 129#define STDIN           pfd[0]
 130#define NETWORK         pfd[1]
 131#define STDIN_READY()   (pfd[0].revents & (POLLIN|POLLERR|POLLHUP))
 132#define NETWORK_READY() (pfd[1].revents & (POLLIN|POLLERR|POLLHUP))
 133
 134static int wait_for_input(void)
 135{
 136        if (STDIN.fd == NETWORK.fd) /* means both are -1 */
 137                exit(0);
 138        dbg("polling\n");
 139        STDIN.revents = NETWORK.revents = 0;
 140        return poll(pfd, 2, -1);
 141}
 142
 143static int32 certCb(ssl_t *ssl, psX509Cert_t *cert, int32 alert)
 144{
 145        /* Example to allow anonymous connections based on a define */
 146        if (alert > 0) {
 147                return SSL_ALLOW_ANON_CONNECTION; // = 254
 148        }
 149#if 0
 150        /* Validate the 'not before' and 'not after' dates, etc */
 151        return PS_FAILURE; /* if we don't like this cert */
 152#endif
 153        return PS_SUCCESS;
 154}
 155
 156static void close_conn_and_exit(ssl_t *ssl, int fd)
 157{
 158        unsigned char *buf;
 159        int len;
 160
 161        fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
 162        /* Quick attempt to send a closure alert, don't worry about failure */
 163        if (matrixSslEncodeClosureAlert(ssl) >= 0) {
 164                len = matrixSslGetOutdata(ssl, &buf);
 165                if (len > 0) {
 166                        len = safe_write(fd, buf, len);
 167                        //if (len > 0) {
 168                        //      matrixSslSentData(ssl, len);
 169                        //}
 170                }
 171        }
 172        //matrixSslDeleteSession(ssl);
 173        shutdown(fd, SHUT_WR);
 174        exit(0);
 175}
 176
 177static int encode_data(ssl_t *ssl, const void *data, int len)
 178{
 179        unsigned char *buf;
 180        int available;
 181
 182        available = matrixSslGetWritebuf(ssl, &buf, len);
 183        if (available < 0)
 184                die("matrixSslGetWritebuf\n");
 185        if (len > available)
 186                die("len > available\n");
 187        memcpy(buf, data, len);
 188        if (matrixSslEncodeWritebuf(ssl, len) < 0)
 189                die("matrixSslEncodeWritebuf\n");
 190        return len;
 191}
 192
 193static void flush_to_net(ssl_t *ssl, int fd)
 194{
 195        int rc;
 196        int len;
 197        unsigned char *buf;
 198
 199        while ((len = matrixSslGetOutdata(ssl, &buf)) > 0) {
 200                dbg("writing net %d bytes\n", len);
 201                if (full_write(fd, buf, len) != len)
 202                        die("write to network\n");
 203                rc = matrixSslSentData(ssl, len);
 204                if (rc < 0)
 205                        die("matrixSslSentData\n");
 206        }
 207}
 208
 209static void do_io_until_eof_and_exit(int fd, sslKeys_t *keys)
 210{
 211        int rc;
 212        int len;
 213        uint32_t len32u;
 214        sslSessionId_t *sid;
 215        ssl_t *ssl;
 216        unsigned char *buf;
 217
 218        NETWORK.fd = fd;
 219        /* Note! STDIN.fd is disabled (-1) until SSL handshake is over:
 220         * we do not attempt to feed any user data to MatrixSSL
 221         * before it is ready.
 222         */
 223
 224        matrixSslNewSessionId(&sid);
 225        rc = matrixSslNewClientSession(&ssl, keys, sid, 0, certCb, NULL, NULL, 0);
 226dbg("matrixSslNewClientSession:rc=%d\n", rc);
 227        if (rc != MATRIXSSL_REQUEST_SEND)
 228                die("matrixSslNewClientSession\n");
 229
 230        len = 0; /* only to suppress compiler warning */
 231 again:
 232        switch (rc) {
 233        case MATRIXSSL_REQUEST_SEND:
 234                dbg("MATRIXSSL_REQUEST_SEND\n");
 235                flush_to_net(ssl, fd);
 236                goto poll_input;
 237
 238        case 0:
 239                dbg("rc==0\n");
 240                flush_to_net(ssl, fd);
 241                goto poll_input;
 242
 243        case MATRIXSSL_REQUEST_CLOSE:
 244                /* what does this mean if we are here? */
 245                dbg("MATRIXSSL_REQUEST_CLOSE\n");
 246                close_conn_and_exit(ssl, fd);
 247
 248        case MATRIXSSL_HANDSHAKE_COMPLETE:
 249                dbg("MATRIXSSL_HANDSHAKE_COMPLETE\n");
 250                /* Init complete, can start reading local user's data: */
 251                STDIN.fd = STDIN_FILENO;
 252 poll_input:
 253                wait_for_input();
 254                if (STDIN_READY()) {
 255                        char ibuf[4 * 1024];
 256                        dbg("reading stdin\n");
 257                        len = read(STDIN_FILENO, ibuf, sizeof(ibuf));
 258                        if (len < 0)
 259                                die("read error on stdin\n");
 260                        if (len == 0)
 261                                STDIN.fd = -1;
 262                        else {
 263                                len = encode_data(ssl, ibuf, len);
 264                                if (len) {
 265                                        rc = MATRIXSSL_REQUEST_SEND;
 266dbg("rc=%d\n", rc);
 267                                        goto again;
 268                                }
 269                        }
 270                }
 271 read_network:
 272                if (NETWORK_READY()) {
 273                        dbg("%s%s%s\n",
 274                                (pfd[1].revents & POLLIN)  ? "POLLIN"  : "",
 275                                (pfd[1].revents & POLLERR) ? "|POLLERR" : "",
 276                                (pfd[1].revents & POLLHUP) ? "|POLLHUP" : ""
 277                        );
 278                        len = matrixSslGetReadbuf(ssl, &buf);
 279                        if (len <= 0)
 280                                die("matrixSslGetReadbuf\n");
 281                        dbg("reading net up to %d\n", len);
 282                        len = read(fd, buf, len);
 283                        dbg("reading net:%d\n", len);
 284                        if (len < 0)
 285                                die("read error on network\n");
 286                        if (len == 0) /*eof*/
 287                                NETWORK.fd = -1;
 288                        len32u = len;
 289                        rc = matrixSslReceivedData(ssl, len, &buf, &len32u);
 290dbg("matrixSslReceivedData:rc=%d\n", rc);
 291                        len = len32u;
 292                        if (rc < 0)
 293                                die("matrixSslReceivedData\n");
 294                }
 295                goto again;
 296
 297        case MATRIXSSL_APP_DATA:
 298                dbg("MATRIXSSL_APP_DATA: writing stdout\n");
 299                do {
 300                        if (full_write(STDOUT_FILENO, buf, len) != len)
 301                                die("write to stdout\n");
 302                        len32u = len;
 303                        rc = matrixSslProcessedData(ssl, &buf, &len32u);
 304//this was seen returning rc=0:
 305dbg("matrixSslProcessedData:rc=%d\n", rc);
 306                        len = len32u;
 307                } while (rc == MATRIXSSL_APP_DATA);
 308                if (pfd[1].fd == -1) {
 309                        /* Already saw EOF on network, and we processed
 310                         * and wrote out all ssl data. Signal it:
 311                         */
 312                        close(STDOUT_FILENO);
 313                }
 314                goto again;
 315
 316        case MATRIXSSL_REQUEST_RECV:
 317                dbg("MATRIXSSL_REQUEST_RECV\n");
 318                wait_for_input();
 319                goto read_network;
 320
 321        case MATRIXSSL_RECEIVED_ALERT:
 322                dbg("MATRIXSSL_RECEIVED_ALERT\n");
 323                /* The first byte of the buffer is the level */
 324                /* The second byte is the description */
 325                if (buf[0] == SSL_ALERT_LEVEL_FATAL)
 326                        die("Fatal alert\n");
 327                /* Closure alert is normal (and best) way to close */
 328                if (buf[1] == SSL_ALERT_CLOSE_NOTIFY)
 329                        close_conn_and_exit(ssl, fd);
 330                die("Warning alert\n");
 331                len32u = len;
 332                rc = matrixSslProcessedData(ssl, &buf, &len32u);
 333dbg("matrixSslProcessedData:rc=%d\n", rc);
 334                len = len32u;
 335                goto again;
 336
 337        default:
 338                /* If rc < 0 it is an error */
 339                die("bad rc:%d\n", rc);
 340        }
 341}
 342
 343static sslKeys_t* make_keys(void)
 344{
 345        int rc, CAstreamLen;
 346        char *CAstream;
 347        sslKeys_t *keys;
 348
 349        if (matrixSslNewKeys(&keys) < 0)
 350                die("matrixSslNewKeys\n");
 351
 352#ifdef USE_HEADER_KEYS
 353        /*
 354         * In-memory based keys
 355         * Build the CA list first for potential client auth usage
 356         */
 357        CAstream = NULL;
 358        CAstreamLen = sizeof(RSACAS);
 359        if (CAstreamLen > 0) {
 360                CAstream = psMalloc(NULL, CAstreamLen);
 361                memcpy(CAstream, RSACAS, sizeof(RSACAS));
 362        }
 363
 364 #ifdef ID_RSA
 365        rc = matrixSslLoadRsaKeysMem(keys, RSA2048, sizeof(RSA2048),
 366                        RSA2048KEY, sizeof(RSA2048KEY), (unsigned char*)CAstream,
 367                        CAstreamLen);
 368        if (rc < 0)
 369                die("matrixSslLoadRsaKeysMem\n");
 370 #endif
 371
 372        if (CAstream)
 373                psFree(CAstream);
 374#endif /* USE_HEADER_KEYS */
 375        return keys;
 376}
 377
 378int main(int argc, char **argv)
 379{
 380        int fd;
 381        char *fd_str;
 382
 383        if (!argv[1])
 384                die("Syntax error\n");
 385        if (argv[1][0] != '-')
 386                die("Syntax error\n");
 387        if (argv[1][1] != 'd')
 388                die("Syntax error\n");
 389        fd_str = argv[1] + 2;
 390        if (!fd_str[0])
 391                fd_str = argv[2];
 392        if (!fd_str || fd_str[0] < '0' || fd_str[0] > '9')
 393                die("Syntax error\n");
 394
 395        fd = atoi(fd_str);
 396        if (fd < 3)
 397                die("Syntax error\n");
 398
 399        if (matrixSslOpen() < 0)
 400                die("matrixSslOpen\n");
 401
 402        do_io_until_eof_and_exit(fd, make_keys());
 403        /* does not return */
 404
 405        return 0;
 406}
 407