tiptunnel.c 20.7 KB
Newer Older
1 2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2004, 2005, 2006 University of Utah and the Flux Group.
4 5
 * All rights reserved.
 */
Timothy Stack's avatar
 
Timothy Stack committed
6 7 8 9

#include "config.h"

#include <errno.h>
Chad Barb's avatar
Chad Barb committed
10 11 12 13
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
Timothy Stack's avatar
 
Timothy Stack committed
14 15 16 17 18 19 20 21
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <assert.h>
#include <paths.h>

#include <sys/time.h>
Chad Barb's avatar
Chad Barb committed
22

Timothy Stack's avatar
 
Timothy Stack committed
23
#include <sys/types.h>
Chad Barb's avatar
Chad Barb committed
24 25 26 27 28
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

Chad Barb's avatar
 
Chad Barb committed
29 30
#ifdef WITHSSL
#include <openssl/ssl.h>
Chad Barb's avatar
 
Chad Barb committed
31
#include <openssl/err.h>
Chad Barb's avatar
 
Chad Barb committed
32
#endif /* WITHSSL */
Chad Barb's avatar
Chad Barb committed
33 34 35

#include "capdecls.h"

Chad Barb's avatar
 
Chad Barb committed
36
int localmode = 0;
Timothy Stack's avatar
 
Timothy Stack committed
37
int uploadmode = 0;
38

Timothy Stack's avatar
 
Timothy Stack committed
39
#define ACLDIR "/var/log/tiplogs"
Chad Barb's avatar
 
Chad Barb committed
40
#define DEFAULT_PROGRAM "xterm -T TIP -e telnet localhost @s"
41

Chad Barb's avatar
Chad Barb committed
42 43 44 45 46 47 48 49 50 51 52 53
int debug = 0;
int allowRemote = 0;

unsigned short port = 0;
int sock = 0;

unsigned short tunnelPort = 0;
int tunnelSock = 0;

char * programToLaunch = NULL;
char * hostname = NULL;

Timothy Stack's avatar
 
Timothy Stack committed
54 55 56
char * certfile = NULL;
char * user = NULL;

Chad Barb's avatar
Chad Barb committed
57 58
secretkey_t key;

Chad Barb's avatar
 
Chad Barb committed
59 60 61 62 63 64
typedef int WriteFunc( void * data, int size );
typedef int ReadFunc( void * data, int size );

WriteFunc * writeFunc;
ReadFunc  * readFunc;

Chad Barb's avatar
Chad Barb committed
65 66 67 68 69 70 71
void usage();
void loadAcl( const char * filename );
void doConnect();
void doAuthenticate();
void doCreateTunnel();
void doTunnelConnection();

Chad Barb's avatar
 
Chad Barb committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
int writeNormal( void * data, int size );
int readNormal( void * data, int size );

#ifdef WITHSSL

void initSSL();
void sslConnect();

int writeSSL( void * data, int size );
int readSSL( void * data, int size );

SSL * ssl;
SSL_CTX * ctx;

int usingSSL = 0;
char * certString = NULL;

#endif /* WITHSSL */
Chad Barb's avatar
Chad Barb committed
90

Timothy Stack's avatar
 
Timothy Stack committed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
#if defined(TIPPTY)

#if defined(__FreeBSD__)
# include <libutil.h>
#endif

#if defined(linux)
# if !defined(INFTIM)
#  define INFTIM -1
# endif
# include <pty.h>
# include <utmp.h>
#endif

#include <poll.h>
#include <termios.h>

#define TIPDIR "/dev/tip"
#define POLL_HUP_INTERVAL (250) /* ms */

typedef struct {
    char data[4096];
    size_t inuse;
} buffer_t;

static void pack_buffer(buffer_t *buffer, int amount);
static void dotippty(char *nodename);

static char pidfilename[1024] = "";
120
static char linkpath[128] = "";
Timothy Stack's avatar
 
Timothy Stack committed
121

122
static void cleanup_atexit(void)
Timothy Stack's avatar
 
Timothy Stack committed
123 124 125
{
  if (strlen(pidfilename) > 0)
    unlink(pidfilename);
126 127 128 129 130 131
  if (strlen(linkpath) > 0)
    unlink(linkpath);
}

static void sigquit(int sig)
{
Timothy Stack's avatar
 
Timothy Stack committed
132 133 134 135
  exit(0);
}
#endif

Chad Barb's avatar
Chad Barb committed
136 137
int main( int argc, char ** argv )
{
Chad Barb's avatar
 
Chad Barb committed
138
  const char * name = argv[0];
139
  char * aclfile = (char *) NULL;
Chad Barb's avatar
Chad Barb committed
140 141
  int op;

Timothy Stack's avatar
 
Timothy Stack committed
142
#if defined(LOCALBYDEFAULT) || defined(TIPPTY)
Chad Barb's avatar
 
Chad Barb committed
143
  localmode++;
144 145 146
#if defined(TIPPTY)
  atexit(cleanup_atexit);
#endif
Chad Barb's avatar
 
Chad Barb committed
147 148
#endif

149
  while ((op = getopt( argc, argv, "hlp:rdu:c:a:" )) != -1) {
Chad Barb's avatar
Chad Barb committed
150
    switch (op) {
Chad Barb's avatar
 
Chad Barb committed
151 152 153
      case 'h':
        usage(name);
        break;
154 155 156
      case 'a':
	aclfile = optarg;
        break;
Chad Barb's avatar
 
Chad Barb committed
157 158 159
      case 'l':
	localmode++;
	break;
Chad Barb's avatar
Chad Barb committed
160 161 162 163 164 165 166 167 168
      case 'p':
        tunnelPort = atoi( optarg );
	break;
      case 'd':
	debug++;
	break;
      case 'r':
	allowRemote++;
	break;
Timothy Stack's avatar
 
Timothy Stack committed
169 170 171 172 173 174 175 176 177 178
#ifdef WITHSSL
      case 'u':
	user = optarg;
	uploadmode++;
	usingSSL++;
	break;
      case 'c':
	certfile = optarg;
	break;
#endif
Chad Barb's avatar
Chad Barb committed
179 180 181 182 183 184 185
    }
  }

  argc -= optind;
  argv += optind;

  if (argc < 1) {
Chad Barb's avatar
 
Chad Barb committed
186
    usage( name );
Chad Barb's avatar
Chad Barb committed
187 188 189 190
  }

  if (argc > 1) {
    programToLaunch = strdup( argv[1] );    
191
  } else {
Chad Barb's avatar
 
Chad Barb committed
192
    programToLaunch = strdup( DEFAULT_PROGRAM );
Chad Barb's avatar
Chad Barb committed
193 194
  }

Chad Barb's avatar
 
Chad Barb committed
195
  if (localmode) {
196 197 198 199 200 201 202
    if (aclfile)
      loadAcl( aclfile );
    else {
      char localAclName[1024];
      sprintf( localAclName, "%s/%s.acl", ACLDIR, argv[0] );
      loadAcl( localAclName );
    }
Chad Barb's avatar
 
Chad Barb committed
203 204 205
  } else {
    loadAcl( argv[0] );
  }
Chad Barb's avatar
Chad Barb committed
206

Chad Barb's avatar
 
Chad Barb committed
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
#ifdef WITHSSL
  if (usingSSL) { 
    writeFunc = writeSSL;
    readFunc  = readSSL;
    initSSL();
    doConnect();
    sslConnect();
  } else
#endif /* WITHSSL */
  {
    /* either there is no SSL, or they're not using it. */
    writeFunc = writeNormal;
    readFunc  = readNormal;
    doConnect();
  }

Timothy Stack's avatar
 
Timothy Stack committed
223 224 225 226 227 228
  if (user && (getuid() == 0)) {
    struct passwd *pw;
    struct group *gr;
    uid_t uid;
    int rc;
    
229 230 231 232 233 234
    if (sscanf(user, "%d", &uid) == 1)
      pw = getpwuid(uid);
    else
      pw = getpwnam(user);

    if (pw == NULL) {
Timothy Stack's avatar
 
Timothy Stack committed
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
      fprintf(stderr, "invalid user: %s %d\n", user, uid);
      exit(1);
    }
    
    if ((gr = getgrgid(pw->pw_gid)) == NULL) {
      fprintf(stderr, "invalid group: %d\n", pw->pw_gid);
      exit(1);
    }
    
    /*
     * Initialize the group list, and then flip to uid.
     */
    if (setgid(pw->pw_gid) ||
	initgroups(user, pw->pw_gid) ||
	setuid(pw->pw_uid)) {
      fprintf(stderr, "Could not become user: %s\n", user);
      exit(1);
    }
  }

  if (uploadmode) {
256
    int fd = STDIN_FILENO;
Timothy Stack's avatar
 
Timothy Stack committed
257 258 259 260 261 262 263 264 265 266 267 268 269

    if ((strcmp(argv[1], "-") != 0) && (fd = open(argv[1], O_RDONLY)) < 0) {
      fprintf(stderr, "Cannot open file: %s\n", argv[1]);
      exit(1);
    }
    else {
      char buf[4096];
      int rc;

      while ((rc = read(fd, buf, sizeof(buf))) > 0) {
	writeFunc(buf, rc);
      }
      close(fd);
270
      fd = -1;
Timothy Stack's avatar
 
Timothy Stack committed
271 272 273 274
    }
    exit(0);
  }
  
Chad Barb's avatar
Chad Barb committed
275
  doAuthenticate();
Timothy Stack's avatar
 
Timothy Stack committed
276 277

#if defined(TIPPTY)
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
  if (!debug) {
    FILE *file;
    
    daemon(0, 0);
    signal(SIGINT, sigquit);
    signal(SIGTERM, sigquit);
    signal(SIGQUIT, sigquit);
    snprintf(pidfilename, sizeof(pidfilename),
	     "%s/tippty.%s.pid",
	     _PATH_VARRUN, argv[0]);
    if ((file = fopen(pidfilename, "w")) != NULL) {
      fprintf(file, "%d\n", getpid());
      fclose(file);
    }
  }

Timothy Stack's avatar
 
Timothy Stack committed
294 295
  dotippty(argv[0]);
#else
Chad Barb's avatar
Chad Barb committed
296
  doCreateTunnel();
Chad Barb's avatar
 
Chad Barb committed
297

Chad Barb's avatar
Chad Barb committed
298 299
  if (programToLaunch) {
    char portString[12];
300

Chad Barb's avatar
Chad Barb committed
301
    sprintf( portString, "%i", tunnelPort );
302

Chad Barb's avatar
 
Chad Barb committed
303 304 305 306 307 308 309 310 311 312 313 314 315 316
    if (localmode) {
      if (!fork()) {
	char * foo[4];
	foo[0] = "telnet";
	foo[1] = "localhost";
	foo[2] = portString;
	foo[3] = NULL;
	execvp( "telnet", foo );
	exit(666);
      }
    } else {
      char runString[1024];
      char * foo;

Chad Barb's avatar
 
Chad Barb committed
317
      for (foo = programToLaunch; *foo; foo++) {
318
	if (*foo == '@') { *foo = '%'; break; }
Chad Barb's avatar
 
Chad Barb committed
319 320 321
      }

      sprintf(runString, programToLaunch, portString);
Chad Barb's avatar
 
Chad Barb committed
322 323

      if (debug) printf("Running '%s'\n", runString);
324 325 326 327
      
      if (! fork()) {
        exit(system( runString ));
      }
Chad Barb's avatar
 
Chad Barb committed
328 329
    }
   
330
    /*
Chad Barb's avatar
Chad Barb committed
331 332 333 334
    if (!fork()) {
      execlp( programToLaunch, 
	      programToLaunch, "localhost", portString, NULL );
    }
335
    */
Chad Barb's avatar
Chad Barb committed
336
  }
Chad Barb's avatar
 
Chad Barb committed
337
  //if (localmode) { sleep(3); }
Chad Barb's avatar
Chad Barb committed
338 339
  doTunnelConnection();
  if (debug) { printf("tiptunnel closing.\n"); }
Timothy Stack's avatar
 
Timothy Stack committed
340
#endif
Chad Barb's avatar
Chad Barb committed
341 342
}

Chad Barb's avatar
 
Chad Barb committed
343
void usage(const char * name)
Chad Barb's avatar
Chad Barb committed
344
{
Timothy Stack's avatar
 
Timothy Stack committed
345
#if defined(LOCALBYDEFAULT)
Chad Barb's avatar
 
Chad Barb committed
346 347 348 349 350 351 352 353

  printf("Usage:\n"
	 "%s [-d] <pcname>\n"
	 "-d               turns on more verbose debug messages\n"
	 "<pcname>         name of machine to connect to (e.g. \"pc42\")\n",
	 name
	 );

Timothy Stack's avatar
 
Timothy Stack committed
354 355 356 357 358 359
#elif defined(TIPPTY)

  printf("Usage:\n"
	 "%s [-d] <node>\n",
	 name);
  
Chad Barb's avatar
 
Chad Barb committed
360 361
#else

Chad Barb's avatar
 
Chad Barb committed
362 363 364 365 366
  printf("No aclfile specified.\n"
         "If you are using a web browser, perhaps\n"
         "it is not properly configured to pass\n"
         "an argument to tiptunnel.\n\n");

Chad Barb's avatar
Chad Barb committed
367
  printf("Usage:\n"
Chad Barb's avatar
 
Chad Barb committed
368 369 370 371 372
	 "%s [-l] [-p <portnum>] [-d] [-r] aclfile [<program>]\n"
	 "-l               enables local mode.\n"
	 "                 acl file will be looked for in:\n"
	 "                 /var/log/tiplogs/<aclfile>.acl\n"
	 "                 (also, server certificate will not be checked)\n"
Chad Barb's avatar
Chad Barb committed
373
	 "-p <portnum>     specifies tunnel port number\n"
Chad Barb's avatar
 
Chad Barb committed
374
	 "-d               turns on more verbose debug messages\n"
Chad Barb's avatar
Chad Barb committed
375
	 "-r               allows connections to tunnel from non-localhost\n"
Timothy Stack's avatar
 
Timothy Stack committed
376
	 "-u <user>        upload mode\n"
Chad Barb's avatar
Chad Barb committed
377
	 "\n"
Chad Barb's avatar
 
Chad Barb committed
378 379
	 "<program>        (non-local-mode only)\n"
	 "                 path of program to launch; default is\n"
Chad Barb's avatar
 
Chad Barb committed
380 381
	 "                 \"%s\"\n"
         "                 ('@s' indicates where to put port number.)\n",
Chad Barb's avatar
 
Chad Barb committed
382
	 name,
383
         DEFAULT_PROGRAM
Chad Barb's avatar
 
Chad Barb committed
384
	 //"-k keeps accepting connections until killed"
Chad Barb's avatar
Chad Barb committed
385
	 );
Chad Barb's avatar
 
Chad Barb committed
386 387

#endif
Chad Barb's avatar
Chad Barb committed
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
  exit(-1);
}

void loadAcl( const char * filename )
{
  FILE * aclFile = fopen( filename, "r" );
  char b1[256];
  char b2[256];

  if (!aclFile) {
    perror("open");
    fprintf( stderr, "Error opening ACL file '%s'\n", filename );
    exit(-1);
  }

  if (debug) { printf("Opened ACL file '%s'\n", filename ); }
  
  bzero( &key, sizeof( key ) );

  while (fscanf(aclFile, "%s %s\n", &b1, &b2) != EOF) {
408
    if ( strcmp(b1, "host:") == 0 || strcmp(b1, "server:") == 0 ) {
Timothy Stack's avatar
 
Timothy Stack committed
409 410
      if (!uploadmode)
	hostname = strdup( b2 );
411
    } else if ( strcmp(b1, "port:") == 0 || strcmp(b1, "portnum:") == 0 ) {
Timothy Stack's avatar
 
Timothy Stack committed
412 413
      if (!uploadmode)
	port = atoi( b2 );
Chad Barb's avatar
Chad Barb committed
414 415
    } else if ( strcmp(b1, "keylen:") == 0 ) {
      key.keylen = atoi( b2 );
416
    } else if ( strcmp(b1, "key:") == 0 || strcmp(b1, "keydata:") == 0) {
Chad Barb's avatar
Chad Barb committed
417
      strcpy( key.key, b2 );
Chad Barb's avatar
 
Chad Barb committed
418
#ifdef WITHSSL
Timothy Stack's avatar
 
Timothy Stack committed
419 420 421 422 423 424
    } else if ( strcmp(b1, "uphost:") == 0 ) {
      if (uploadmode)
	hostname = strdup( b2 );
    } else if ( strcmp(b1, "upport:") == 0 ) {
      if (uploadmode)
	port = atoi( b2 );
Chad Barb's avatar
 
Chad Barb committed
425 426 427 428 429
    } else if ( strcmp(b1, "ssl-server-cert:") == 0 ) {
      if (debug) { printf("Using SSL to connect to capture.\n"); }
      certString = strdup( b2 );
      usingSSL++;
#endif /* WITHSSL */
Chad Barb's avatar
Chad Barb committed
430
    } else {
431
      /* fprintf(stderr, "Ignored unknown ACL: %s %s\n", b1, b2); */
Chad Barb's avatar
Chad Barb committed
432 433
    }
  }
434 435 436 437
  fclose(aclFile);

  if (!key.keylen)
    key.keylen = strlen(key.key);
Chad Barb's avatar
Chad Barb committed
438

439
  if (!port || !hostname || !strlen(key.key)) {
Chad Barb's avatar
Chad Barb committed
440 441 442 443 444 445
    fprintf(stderr, "Incomplete ACL\n");
    exit(-1);
  }

}

Chad Barb's avatar
 
Chad Barb committed
446 447 448 449 450 451 452 453 454 455
int writeNormal( void * data, int size )
{
  return write( sock, data, size );
}

int readNormal( void * data, int size )
{
  return read( sock, data, size );
}

Chad Barb's avatar
Chad Barb committed
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
void doConnect()
{
  struct sockaddr_in name;
  struct hostent *he;

  name.sin_family = AF_INET;
  name.sin_port   = htons(port);

  he = gethostbyname(hostname);   
  if (!he) {
    fprintf(stderr, "Unknown hostname %s: %s\n",
	    hostname, hstrerror(h_errno));
    exit(-1);
  }
  
  memcpy ((char *)&name.sin_addr, he->h_addr, he->h_length);
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0) {
    perror("socket");
    exit(-1);
  }

  if (connect( sock, (struct sockaddr *)&name, sizeof(name)) < 0) {
    close( sock );
    perror("connect");
    exit(-1);
  }
}
  
void doAuthenticate()
{
  capret_t capret;

Chad Barb's avatar
 
Chad Barb committed
489 490 491 492
  if ( writeFunc( /*sock,*/ &key, sizeof(key) ) == sizeof(key) &&
       readFunc( /*sock,*/ &capret, sizeof(capret)) > 0 ) {
    switch( capret ) {
    case CAPOK: 
Chad Barb's avatar
Chad Barb committed
493
      return;
Chad Barb's avatar
 
Chad Barb committed
494
    case CAPBUSY:       
Chad Barb's avatar
Chad Barb committed
495
      fprintf(stderr, "Tip line busy.\n");
Chad Barb's avatar
 
Chad Barb committed
496 497 498 499 500 501 502
      break;
    case CAPNOPERM:
      fprintf(stderr, "You do not have permission. No TIP for you!\n");
      break;
    default:
      fprintf(stderr, "Unknown return code 0x%02x.\n", (unsigned int)capret);
      return; // XXX Final version should break here. 
Chad Barb's avatar
Chad Barb committed
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
    }
  } else {
    // write or read hosed.
    fprintf(stderr, "Error authenticating\n");
  }

  close( sock );
  exit(-1);
}


void doCreateTunnel()
{
  int i;
  struct sockaddr_in name;

  tunnelSock = socket(AF_INET, SOCK_STREAM, 0);
  if (tunnelSock < 0) { 
    perror("socket");
    exit(-1);
  }

  i = 1;
  if (setsockopt(tunnelSock, SOL_SOCKET, SO_REUSEADDR,
		 (char *)&i, sizeof(i)) < 0) {
    perror("setsockopt: SO_REUSEADDR");
    exit(-1);
  }

  name.sin_family = AF_INET;
  name.sin_addr.s_addr = INADDR_ANY;
  name.sin_port = htons( tunnelPort );
  if (bind(tunnelSock, (struct sockaddr *) &name, sizeof(name))) {
    perror("bind");
    exit(-1);
  }

  i = sizeof(name);
  if (getsockname(tunnelSock, (struct sockaddr *)&name, &i)) {
    perror("getsockname");
    exit(-1);
  }

  tunnelPort = ntohs(name.sin_port);

Chad Barb's avatar
 
Chad Barb committed
548
  if (debug) { printf("Listening on port %i.\n", tunnelPort); }
Chad Barb's avatar
Chad Barb committed
549 550 551 552 553 554 555 556 557

  if (listen(tunnelSock, 1) < 0) {
    perror("listen");
    exit(-1);
  }
}

void doTunnelConnection()
{
Chad Barb's avatar
 
Chad Barb committed
558
  int i;
Chad Barb's avatar
Chad Barb committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
  int conSock;
  struct sockaddr_in name;
  socklen_t namelen = sizeof( name );

acceptor:
  conSock = accept( tunnelSock, (struct sockaddr *)&name, &namelen );

  if (name.sin_family != AF_INET) {
    fprintf(stderr,"Error: non AF_INET connection.\n");
    exit(-1);
  }

  if (!allowRemote && 
      ntohl( name.sin_addr.s_addr ) != INADDR_LOOPBACK) {
    const char reject[] = 
      "Connection attempted from non-local machine (ignored.)\n"
      "Use -r switch to allow non-local connections.\n";
    write( conSock, reject, sizeof( reject ) );
    fprintf( stderr, "%s", reject );
    close( conSock );
    goto acceptor;
  }

  if (conSock < 0 ) {
    perror("accept");
    exit(-1);
  }

Chad Barb's avatar
 
Chad Barb committed
587 588 589 590 591
  if (conSock == 0) {
    if (debug) { printf("Zero consock. Retrying.\n" ); }
    goto acceptor;
  }

Chad Barb's avatar
 
Chad Barb committed
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
  { 
    /*
      lets pretend that we are a telnetd (RFC854).. 
      It will be crackling good fun! 

      we enable "SUPPRESS GO AHEAD" and "ECHO",
      thus (through something commonly called "kludge line mode")
      telling the client _not_ to send a line at a time (and not to echo locally.) 

      I'm actually not totally sure why this works, but I'm afraid if I 
      think too hard about it, it will stop.
    */

    const char telnet_commands[] = 
    { 
      // IAC WILL SUPPRESS GO AHEAD:
      255, 251, 3,
      // IAC WILL ECHO:
      255, 251, 1   
    };

    write( conSock, telnet_commands, sizeof( telnet_commands ) );
  }

Chad Barb's avatar
Chad Barb committed
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
  while (1) {
    fd_set fds;
    char buf[4096];
    
    FD_ZERO( &fds );
    FD_SET( conSock, &fds );
    FD_SET( sock, &fds );
    
    if (select( MAX( conSock, sock ) + 1, &fds, NULL, NULL, NULL ) < 0) {
      perror("select");
      exit(-1);
    }

    if ( FD_ISSET( conSock, &fds ) ) {
      int got = read( conSock, buf, 4096);
      if (got <= 0) {
	if (got < 0) { perror("read conSock"); }
	if (debug) { printf("conSock read %i; exiting.\n", got); }
	return;
      }

Chad Barb's avatar
 
Chad Barb committed
637

Chad Barb's avatar
 
Chad Barb committed
638 639 640 641 642 643 644 645
      /*  
	  we could implement some kind of search to remove 
	  "IAC DO SUPPRESS GO AHEAD" and "IAC DO ECHO" bytes from the stream,
	  or just trash any packet starting in "IAC" (255).
	  Yeah.
      */

      if ((unsigned char)buf[0] != 255) {
646 647 648 649 650 651 652 653 654 655 656 657 658 659

	if (buf[got - 1] == '\0') { got--; }

	/*
	for(i = 0; i < got; i++ ) {
	  if ((unsigned char)buf[i] > 127 || 
	      (unsigned char)buf[i] < 32) {
	    printf("Special to server %i\n", 
		   (unsigned int)(unsigned char)buf[i] );
	    //buf[i] = '#';
	  }
	}
	*/

Chad Barb's avatar
 
Chad Barb committed
660 661 662 663
	if (writeFunc( /*sock,*/ buf, got ) < 0) {
	  perror("write sock");
	  return;
	}
664
      } 
Chad Barb's avatar
Chad Barb committed
665 666 667
    }

    if ( FD_ISSET( sock, &fds ) ) {
Chad Barb's avatar
 
Chad Barb committed
668
      int got = readFunc( /*sock,*/ buf, 4096);
Chad Barb's avatar
Chad Barb committed
669 670 671 672 673 674
      if (got <= 0) {
	if (got < 0) { perror("read sock"); }
	if (debug) { printf("sock read %i; exiting.\n", got); }
	return;
      }

675
      //printf("%i server->consock\n", got );
Chad Barb's avatar
 
Chad Barb committed
676

677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
      if ((unsigned char)buf[0] != 255) {
	/*
	for(i = 0; i < got; i++ ) {
	  if (buf[i] == '^') { buf[i] = '!'; }
	  if ((unsigned char)buf[i] > 127 || 
	      (unsigned char)buf[i] < 32) {
	    printf("Special from server %i\n", 
		   (unsigned int)(unsigned char)buf[i] );
	    //buf[i] = '#';
	  }
	}
	*/
	if (write( conSock, buf, got ) < 0) {
	  perror("write conSock");
	  return;
	}
Chad Barb's avatar
Chad Barb committed
693 694 695 696
      }
    }
  }
}
Chad Barb's avatar
 
Chad Barb committed
697 698 699 700


#ifdef WITHSSL

Timothy Stack's avatar
 
Timothy Stack committed
701 702
#define DEFAULT_CERTFILE TBROOT"/etc/capture.pem"

Chad Barb's avatar
 
Chad Barb committed
703 704 705 706
void initSSL()
{
  SSL_library_init();
  ctx = SSL_CTX_new( SSLv23_method() );
Chad Barb's avatar
 
Chad Barb committed
707
  SSL_load_error_strings();
Timothy Stack's avatar
 
Timothy Stack committed
708 709 710 711 712 713 714 715 716 717 718 719 720 721

  if (uploadmode) {
    if (!certfile) { certfile = DEFAULT_CERTFILE; }
    
    if (SSL_CTX_use_certificate_file( ctx, certfile, SSL_FILETYPE_PEM ) <= 0) {
      fprintf( stderr, "Could not load %s as certificate file.", certfile );
      exit(1);
    }
    
    if (SSL_CTX_use_PrivateKey_file( ctx, certfile, SSL_FILETYPE_PEM ) <= 0) {
      fprintf( stderr, "Could not load %s as key file.", certfile );
      exit(1);
    }
  }
Chad Barb's avatar
 
Chad Barb committed
722 723 724 725 726 727 728 729 730 731 732
  //  if (!(SSL_CTX_load_verify_location( ctx, CA_LIST, 0 ))
}

void sslConnect()
{
  unsigned char digest[256];
  unsigned int len = sizeof( digest );
  unsigned char digestHex[512];
  X509 * peer;
  BIO * sbio;
  int i;
Chad Barb's avatar
 
Chad Barb committed
733 734 735 736 737 738 739
  
  // inform server of desire to use SSL
  {
    secretkey_t sslHintKey;
    char ret[4];

    sslHintKey.keylen = 7;
Timothy Stack's avatar
 
Timothy Stack committed
740 741 742 743
    if (uploadmode)
      strcpy( sslHintKey.key, "UPLOAD" );
    else
      strncpy( sslHintKey.key, "USESSL", 7 );
Chad Barb's avatar
 
Chad Barb committed
744 745 746 747 748 749 750 751
    write( sock, &sslHintKey, sizeof( sslHintKey ) );
    /*
    if (4 != read( sock, ret, 4 ) || 
	0 != strncmp( ret, "OKAY", 4) ) {
      fprintf( stderr, "Didnt get SSL OKAY from server.\n");
      exit(-1);
    }
    */
Timothy Stack's avatar
 
Timothy Stack committed
752
  }
Chad Barb's avatar
 
Chad Barb committed
753 754 755 756

  ssl = SSL_new( ctx );
  SSL_set_fd( ssl, sock );

Chad Barb's avatar
 
Chad Barb committed
757 758 759 760 761 762 763 764 765 766
  /*
  i = 0;
  while (1) {
    char * n = SSL_get_cipher_list( ssl, i );
    if (!n) { break;}
    printf("Cipher #%i is %s\n", i, n );
    i++;
  }
  */

Chad Barb's avatar
 
Chad Barb committed
767 768
  // sbio = BIO_new_socket( sock, BIO_NOCLOSE );
  // SSL_set_bio( ssl, sbio, sbio );
Timothy Stack's avatar
 
Timothy Stack committed
769
  // sleep(1);
Chad Barb's avatar
 
Chad Barb committed
770 771 772
  
  if (SSL_connect( ssl ) <= 0) {
    fprintf(stderr, "SSL Connect error.\n");
Chad Barb's avatar
 
Chad Barb committed
773
    ERR_print_errors_fp( stderr );
Chad Barb's avatar
 
Chad Barb committed
774 775 776
    exit(-1);
  }

Timothy Stack's avatar
 
Timothy Stack committed
777 778 779
  if (uploadmode)
    return;

Chad Barb's avatar
 
Chad Barb committed
780 781 782 783 784 785 786 787 788
  peer = SSL_get_peer_certificate( ssl );

  // X509_print_fp( stdout );
  // X509_digest( &peer, EVP_sha() , digest, &len );

  //X509_digest( peer, EVP_md5(), digest, &len );
  
  X509_digest( peer, EVP_sha(), digest, &len );

Timothy Stack's avatar
 
Timothy Stack committed
789 790
  X509_free( peer );

Chad Barb's avatar
 
Chad Barb committed
791 792 793 794 795 796 797 798 799 800 801
  for (i = 0; i < len; i++) {
    sprintf( digestHex + (i * 2), "%02x", (unsigned int) digest[i] );
  }

  if (debug) {
    printf("ACL's  cert digest: %s\n"
	   "Server cert digest: %s\n",
	   certString, 
	   digestHex );
  }

Chad Barb's avatar
 
Chad Barb committed
802
  if (/*!localmode && */0 != strcmp( certString, digestHex )) {
Chad Barb's avatar
 
Chad Barb committed
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
    fprintf(stderr, 
	    "Server does not have certificate described in ACL:\n"
	    "ACL's  cert digest: %s\n" 
	    "Server cert digest: %s\n"
	    "Possible man-in-the-middle attack. Aborting.\n\n",
	    certString,
	    digestHex );

    exit(-1);
  }
}


int writeSSL( void * data, int size )
{
  return SSL_write( ssl, data, size );
}

int readSSL( void * data, int size )
{
  return SSL_read( ssl, data, size );
}

#endif /* WITHSSL */
Timothy Stack's avatar
 
Timothy Stack committed
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893

#ifdef TIPPTY

static void pack_buffer(buffer_t *buffer, int amount)
{
    assert(buffer != NULL);
    assert((amount == -1) ||
	   ((amount >= 0) && (amount < sizeof(buffer->data))));
    
    if (amount > 0) {
	buffer->inuse -= amount;
	memmove(buffer->data, &buffer->data[amount], buffer->inuse);
    }
}

static void dotippty(char *nodename)
{
  int rc, master, slave;
  char path[64];

  assert(nodename != NULL);
  assert(strlen(nodename) > 0);
  
  if ((mkdir(TIPDIR, 0755) < 0) && (errno != EEXIST))
    perror("unable to make " TIPDIR);
  
  if ((rc = openpty(&master, &slave, path, NULL, NULL)) < 0) {
    perror("openpty");
    exit(1);
  }
  else {
    struct pollfd pf[2] = {
      [0] = { .fd = sock, .events = POLLIN },
      [1] = { .fd = master, .events = POLLHUP },
    };
    
    buffer_t from_pty = { .inuse = 0 }, to_pty = { .inuse = 0 };
    int tweaked = 0, fd_count = 2;
    struct timeval now, before;
    
    fcntl(master, F_SETFL, O_NONBLOCK);
    fcntl(sock, F_SETFL, O_NONBLOCK);
    close(slave);
    slave = -1;

    if (chmod(path, 0666) < 0)
      perror("unable to change permissions");

    snprintf(linkpath, sizeof(linkpath), "%s/%s", TIPDIR, nodename);
    if ((symlink(path, linkpath) < 0) && (errno != EEXIST))
      perror("unable to create symlink");

    gettimeofday(&now, NULL);
    before = now;
    while ((rc = poll(pf,
		      fd_count,
		      fd_count == 1 ? POLL_HUP_INTERVAL : INFTIM)) >= 0) {
      if (rc == 0) { // Timeout
	/* ... check for a slave connection on the next loop. */
	fd_count = 2;
      }
      else if (pf[1].revents & POLLHUP) {
	struct timeval diff;
	
	/* No slave connection. */
	if (pf[0].revents & POLLIN) // Drain the input side.
	  rc = readFunc(to_pty.data, sizeof(to_pty.data));
894
	if (rc <= 0)
Timothy Stack's avatar
 
Timothy Stack committed
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
	  exit(0);
	if ((pf[0].revents & POLLOUT) && from_pty.inuse > 0) {
	  // Drain our buffer to the output.
	  rc = writeFunc(from_pty.data, from_pty.inuse);
	  if (rc < 0)
	    exit(0);
	  pack_buffer(&from_pty, rc);
	}
	to_pty.inuse = 0; // Nowhere to send.
	gettimeofday(&now, NULL);
	timersub(&now, &before, &diff);
	if (diff.tv_sec > 0 || diff.tv_usec > (POLL_HUP_INTERVAL * 100)) {
	  before = now;
	  fd_count = 2;
	}
	else {
	  fd_count = 1; // Don't poll the master and turn on timeout.
	}
	tweaked = 0;
      }
      else {
	if (!tweaked) {
917 918 919 920 921
	  /*
	   * XXX This next bit is a brutal hack to forcefully turn off echo on
	   * the pseudo terminal.  Otherwise we get a nasty loop of data
	   * echoing back and forth.
	   */
Timothy Stack's avatar
 
Timothy Stack committed
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
	  if ((slave = open(path, O_RDONLY)) >= 0) {
	    struct termios tio;
	    
	    tcgetattr(slave, &tio);
	    cfmakeraw(&tio);
	    tio.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ECHOCTL|ECHOKE);
	    tcsetattr(slave, TCSANOW, &tio);
	    close(slave);
	    slave = -1;
	    
	    tweaked = 1;
	  }
	}
	if (pf[1].revents & POLLIN) {
	  rc = read(pf[1].fd,
		    &from_pty.data[from_pty.inuse],
		    sizeof(from_pty.data) - from_pty.inuse);
	  if (rc > 0)
	    from_pty.inuse += rc;
	}
	if (pf[1].revents & POLLOUT) {
	  rc = write(pf[1].fd, to_pty.data, to_pty.inuse);
	  pack_buffer(&to_pty, rc);
	}
	if (pf[0].revents & POLLIN) {
	  rc = readFunc(&to_pty.data[to_pty.inuse],
			sizeof(to_pty.data) - to_pty.inuse);
	  if (rc >= 0)
	    to_pty.inuse += rc;
	  else
	    exit(0);
	}
	if (pf[0].revents & POLLOUT) {
	  rc = writeFunc(from_pty.data, from_pty.inuse);
	  if (rc < 0)
	    exit(0);
	  pack_buffer(&from_pty, rc);
	}
      }
      
      pf[0].events = 0;
      if (to_pty.inuse < sizeof(to_pty.data))
	pf[0].events |= POLLIN;
      if (from_pty.inuse > 0)
	pf[0].events |= POLLOUT;
      
      pf[1].events = POLLHUP;
      if (from_pty.inuse < sizeof(from_pty.data))
	pf[1].events |= POLLIN;
      if (to_pty.inuse > 0)
	pf[1].events |= POLLOUT;
    }
  }
}

#endif