tiptunnel.c 20.7 KB
Newer Older
1 2 3 4 5
/*
 * EMULAB-COPYRIGHT
 * Copyright (c) 2004, 2005 University of Utah and the Flux Group.
 * All rights reserved.
 */
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>
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

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>

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

#include "capdecls.h"

36
int localmode = 0;
37
int uploadmode = 0;
38

39
#define ACLDIR "/var/log/tiplogs"
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;

54 55 56
char * certfile = NULL;
char * user = NULL;

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

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();

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

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] = "";
121

122
static void cleanup_atexit(void)
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)
{
132 133 134 135
  exit(0);
}
#endif

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

142
#if defined(LOCALBYDEFAULT) || defined(TIPPTY)
143
  localmode++;
144 145 146
#if defined(TIPPTY)
  atexit(cleanup_atexit);
#endif
147 148
#endif

149
  while ((op = getopt( argc, argv, "hlp:rdu:c:a:" )) != -1) {
Chad Barb's avatar
Chad Barb committed
150
    switch (op) {
151 152 153
      case 'h':
        usage(name);
        break;
154 155 156
      case 'a':
	aclfile = optarg;
        break;
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;
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) {
186
    usage( name );
Chad Barb's avatar
Chad Barb committed
187 188 189 190
  }

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

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 );
    }
203 204 205
  } else {
    loadAcl( argv[0] );
  }
Chad Barb's avatar
Chad Barb committed
206

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();
  }

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) {
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;
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;
271 272 273 274
    }
    exit(0);
  }
  
Chad Barb's avatar
Chad Barb committed
275
  doAuthenticate();
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);
    }
  }

294 295
  dotippty(argv[0]);
#else
Chad Barb's avatar
Chad Barb committed
296
  doCreateTunnel();
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

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;

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

      sprintf(runString, programToLaunch, portString);
322 323 324 325 326 327 328

      if (debug) printf("Running '%s'\n", runString);

      for (foo = runString; *foo; foo++);
      foo[0] = ' ';
      foo[1] = '&';
      foo[2] = '\0';
329

330 331 332
      system( runString );
    }
   
333
    /*
Chad Barb's avatar
Chad Barb committed
334 335 336 337
    if (!fork()) {
      execlp( programToLaunch, 
	      programToLaunch, "localhost", portString, NULL );
    }
338
    */
Chad Barb's avatar
Chad Barb committed
339
  }
340
  //if (localmode) { sleep(3); }
Chad Barb's avatar
Chad Barb committed
341 342
  doTunnelConnection();
  if (debug) { printf("tiptunnel closing.\n"); }
343
#endif
Chad Barb's avatar
Chad Barb committed
344 345
}

346
void usage(const char * name)
Chad Barb's avatar
Chad Barb committed
347
{
348
#if defined(LOCALBYDEFAULT)
349 350 351 352 353 354 355 356

  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
	 );

357 358 359 360 361 362
#elif defined(TIPPTY)

  printf("Usage:\n"
	 "%s [-d] <node>\n",
	 name);
  
363 364
#else

365 366 367 368 369
  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
370
  printf("Usage:\n"
371 372 373 374 375
	 "%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
376
	 "-p <portnum>     specifies tunnel port number\n"
377
	 "-d               turns on more verbose debug messages\n"
Chad Barb's avatar
Chad Barb committed
378
	 "-r               allows connections to tunnel from non-localhost\n"
379
	 "-u <user>        upload mode\n"
Chad Barb's avatar
Chad Barb committed
380
	 "\n"
381 382
	 "<program>        (non-local-mode only)\n"
	 "                 path of program to launch; default is\n"
383 384
	 "                 \"%s\"\n"
         "                 ('@s' indicates where to put port number.)\n",
385
	 name,
386
         DEFAULT_PROGRAM
387
	 //"-k keeps accepting connections until killed"
Chad Barb's avatar
Chad Barb committed
388
	 );
389 390

#endif
Chad Barb's avatar
Chad Barb committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
  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) {
411
    if ( strcmp(b1, "host:") == 0 || strcmp(b1, "server:") == 0 ) {
412 413
      if (!uploadmode)
	hostname = strdup( b2 );
414
    } else if ( strcmp(b1, "port:") == 0 || strcmp(b1, "portnum:") == 0 ) {
415 416
      if (!uploadmode)
	port = atoi( b2 );
Chad Barb's avatar
Chad Barb committed
417 418
    } else if ( strcmp(b1, "keylen:") == 0 ) {
      key.keylen = atoi( b2 );
419
    } else if ( strcmp(b1, "key:") == 0 || strcmp(b1, "keydata:") == 0) {
Chad Barb's avatar
Chad Barb committed
420
      strcpy( key.key, b2 );
421
#ifdef WITHSSL
422 423 424 425 426 427
    } else if ( strcmp(b1, "uphost:") == 0 ) {
      if (uploadmode)
	hostname = strdup( b2 );
    } else if ( strcmp(b1, "upport:") == 0 ) {
      if (uploadmode)
	port = atoi( b2 );
428 429 430 431 432
    } 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
433
    } else {
434
      /* fprintf(stderr, "Ignored unknown ACL: %s %s\n", b1, b2); */
Chad Barb's avatar
Chad Barb committed
435 436
    }
  }
437 438 439 440
  fclose(aclFile);

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

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

}

449 450 451 452 453 454 455 456 457 458
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
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 489 490 491
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;

492 493 494 495
  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
496
      return;
497
    case CAPBUSY:       
Chad Barb's avatar
Chad Barb committed
498
      fprintf(stderr, "Tip line busy.\n");
499 500 501 502 503 504 505
      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
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 548 549 550
    }
  } 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);

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

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

void doTunnelConnection()
{
561
  int i;
Chad Barb's avatar
Chad Barb committed
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 587 588 589
  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);
  }

590 591 592 593 594
  if (conSock == 0) {
    if (debug) { printf("Zero consock. Retrying.\n" ); }
    goto acceptor;
  }

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
  { 
    /*
      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
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
  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;
      }

640

641 642 643 644 645 646 647 648
      /*  
	  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) {
649 650 651 652 653 654 655 656 657 658 659 660 661 662

	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] = '#';
	  }
	}
	*/

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

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

678
      //printf("%i server->consock\n", got );
679

680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
      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
696 697 698 699
      }
    }
  }
}
700 701 702 703


#ifdef WITHSSL

704 705
#define DEFAULT_CERTFILE TBROOT"/etc/capture.pem"

706 707 708 709
void initSSL()
{
  SSL_library_init();
  ctx = SSL_CTX_new( SSLv23_method() );
710
  SSL_load_error_strings();
711 712 713 714 715 716 717 718 719 720 721 722 723 724

  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);
    }
  }
725 726 727 728 729 730 731 732 733 734 735
  //  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;
736 737 738 739 740 741 742
  
  // inform server of desire to use SSL
  {
    secretkey_t sslHintKey;
    char ret[4];

    sslHintKey.keylen = 7;
743 744 745 746
    if (uploadmode)
      strcpy( sslHintKey.key, "UPLOAD" );
    else
      strncpy( sslHintKey.key, "USESSL", 7 );
747 748 749 750 751 752 753 754
    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);
    }
    */
755
  }
756 757 758 759

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

760 761 762 763 764 765 766 767 768 769
  /*
  i = 0;
  while (1) {
    char * n = SSL_get_cipher_list( ssl, i );
    if (!n) { break;}
    printf("Cipher #%i is %s\n", i, n );
    i++;
  }
  */

770 771
  // sbio = BIO_new_socket( sock, BIO_NOCLOSE );
  // SSL_set_bio( ssl, sbio, sbio );
772
  // sleep(1);
773 774 775
  
  if (SSL_connect( ssl ) <= 0) {
    fprintf(stderr, "SSL Connect error.\n");
776
    ERR_print_errors_fp( stderr );
777 778 779
    exit(-1);
  }

780 781 782
  if (uploadmode)
    return;

783 784 785 786 787 788 789 790 791
  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 );

792 793
  X509_free( peer );

794 795 796 797 798 799 800 801 802 803 804
  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 );
  }

805
  if (/*!localmode && */0 != strcmp( certString, digestHex )) {
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
    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 */
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 894 895 896

#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));
897
	if (rc <= 0)
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
	  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) {
920 921 922 923 924
	  /*
	   * 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.
	   */
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 978 979 980
	  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