int create_shmfile(const char *name,int oflags,mode_t mode){ int fd; if(name[0] != '/'){ bitch("Not an absolute path: %s\n",name); return -1; } #ifdef LIB_COMPAT_FREEBSD // FreeBSD sets certain file descriptor flags based on shm_open(3), so // we need to use it (Linux's just places things on the tmpfs) fd = Shm_open(name,oflags,mode); #else #ifdef LIB_COMPAT_LINUX // We don't want to use shm_open(3) on Linux, because it restricts us // to our IPC namespace's tmpfs (/dev/shm by default), meaning we // (a) collide with other processes and (b) can't use hugetlbfs. Thus, // we use open(2) directly, but enforce a VM-based filesystem. // FIXME enforce filesystem restrictions on Linux fd = OpenCreat(name,oflags,mode); #else bitch("No support for %s, %x, %x on this OS\n",name,oflags,mode); fd = -1; #endif #endif return fd; }
static struct rr* soa_parse(char *name, long ttl, int type, char *s) { struct rr_soa *rr = getmem(sizeof(*rr)); long long i; rr->mname = extract_name(&s, "mname", 0); if (!rr->mname) return NULL; rr->rname = extract_name(&s, "rname", 0); if (!rr->rname) return NULL; i = extract_integer(&s, "serial", NULL); if (i < 0) return NULL; if (i > 4294967295UL) return bitch("serial is out of range"); rr->serial = i; rr->refresh = extract_timevalue(&s, "refresh"); if (rr->refresh < 0) return NULL; rr->retry = extract_timevalue(&s, "retry"); if (rr->retry < 0) return NULL; rr->expire = extract_timevalue(&s, "expire"); if (rr->expire < 0) return NULL; rr->minimum = extract_timevalue(&s, "minimum"); if (rr->minimum < 0) return NULL; if (ttl < 0 && G.opt.soa_minttl_as_default_ttl) { ttl = rr->minimum; } if (*s) { return bitch("garbage after valid SOA data"); } return store_record(type, name, ttl, rr); }
// Allocate a percentage of available memory for the object listed. Totally // non-exact. The percentage p must satisfy 0 < p <= 100. Initializes returned // memory to 0. void *Palloc(const char *name,size_t osiz,unsigned *num,unsigned p){ void *ret = NULL; size_t rsiz; if(p <= 0 || p > 100){ bitch("Invalid allocation %u%% for %s\n",p,name); return NULL; } pthread_mutex_lock(&memlock); ++allocs_reqd; if((rsiz = size_palloc_req(p,name)) < osiz){ ++allocs_fail; }else{ rsiz -= rsiz % osiz; if((ret = malloc(rsiz)) == NULL){ ++allocs_fail; }else{ ++allocs_used; } } pthread_mutex_unlock(&memlock); if(ret == NULL){ bitch("%%-alloc failed for %zu bytes for %s\n",rsiz,name); }else{ *num = rsiz / osiz; nag("%s: %zu(%p) ok\n",name,rsiz,ret); nag("%u%% -> %zub (%u %zub %s)\n",p,rsiz,*num,osiz,name); } return ret; }
// Provide the maximum memory size in bytes. It will be checked against free // memory (*not* claimed memory; we do not know other reservations), and capped // at 50% thereof. If the limit is far too low for a sane app, this will also // result in a -1 return (mainly to catch mistakes involving units). A limit // of 0 implies capping only wrt known free memory. int limit_memory(size_t s){ int ret = 0; if(memory_usage_limit){ bitch("Not resetting memlimit of %jub\n",memory_usage_limit); return -1; } if(s){ nag("Provided a memory limit of %zub\n",s); }else{ s = DEFAULT_APP_MEMLIMIT; } memory_usage_limit = determine_sysmem(); static_usage = determine_static_usage(); nag("Preexisting allocation total: %zu\n",static_usage); s += static_usage; if(s > memory_usage_limit){ bitch("Capping %zub to syslimit %jub\n",s,memory_usage_limit); ret = -1; }else if(s){ struct rlimit rl = { .rlim_cur = s, .rlim_max = s, }; ret |= Setrlimit(RLIMIT_AS,&rl); memory_usage_limit = s; } nag("Setting memlimit: %ju MiB\n",memory_usage_limit / MIBIBYTE); return ret; }
int parser_byline(int fd,line_parser_cb lcb,void *arg){ int ret = -1,lines = 0; line_parser_ctx ctx; char *line; if(prepare_line_parser(&ctx,fd)){ return -1; } if(!lcb){ nag("Not executing any parser callbacks\n"); } errno = 0; while( (line = line_parser_next(&ctx)) ){ ++lines; if(lcb && lcb(line,arg)){ bitch("Failure in parse function, exiting\n"); goto done; } errno = 0; } if(errno){ bitch("Failure reading data for parsing at line %d\n",lines); }else{ nag("Read %d lines\n",lines); ret = 0; } done: destroy_line_parser(&ctx); return ret; }
// FIXME we need better EV_ERROR handling static inline void handle_kqueue_results(poller *p,const struct kevent *kevents,unsigned events){ unsigned n = 0; // nag("Handling %u events\n",events); for(n = 0 ; n < events ; ++n){ const struct kevent *kv = &kevents[n]; if(kv->filter == EVFILT_SIGNAL){ nag("SignalRX (%s)\n",strsignal(kv->ident)); if(kv->ident == SIGCHLD){ p->sigchldrx += kv->data; handle_sigchld(p); } }else{ pollfd_state *state; int fd = kv->ident; if(fd < 0 || fd >= p->pfds_available){ bitch("Invalid fd %d (max %d)\n",fd,p->pfds_available); inc_stateexceptions(); continue; } state = &p->fdstates[fd]; if(fd != state->pfd.fd){ // We might close a descriptor despite // outstanding events later in the vector. Note // them, but don't take action otherwise. handle_invalidated_kevent(kv,fd,state); }else{ struct timeval tvstart,tvend; int r; Gettimeofday(&tvstart,NULL); state->lastuse_time = tvstart.tv_sec; if(kv->flags & EV_ERROR){ handle_kqueue_error(state); r = -1; }else if(kv->filter == EVFILT_READ){ r = handle_kqueue_read(p,state); }else if(kv->filter == EVFILT_WRITE){ r = handle_kqueue_write(p,state); }else if(kv->filter == EVFILT_TIMER){ // nag("Timer callback %d\n",fd); r = handle_kqueue_timeout(p,state); }else{ bitch("Unknown kevent filter %d\n",kv->filter); inc_stateexceptions(); r = 0; } if(!r){ time_avgmax(&state->pfd_avgmax_time,&tvend,&tvstart); }else{ purge_fd_resulthandler(p,state,fd); } } } } }
static struct rr *naptr_parse(char *name, long ttl, int type, char *s) { struct rr_naptr *rr = getmem(sizeof(*rr)); int i; struct binary_data text; i = extract_integer(&s, "order"); if (i < 0) return NULL; if (i >= 65536) return bitch("order range is not valid"); rr->order = i; i = extract_integer(&s, "preference"); if (i < 0) return NULL; if (i >= 65536) return bitch("preference range is not valid"); rr->preference = i; text = extract_text(&s, "flags"); if (text.length < 0) return NULL; for (i = 0; i < text.length; i++) { if (!isalnum(text.data[i])) { return bitch("flags contains illegal characters"); } } rr->flags = text; text = extract_text(&s, "services"); if (text.length < 0) return NULL; rr->services = text; text = extract_text(&s, "regexp"); if (text.length < 0) return NULL; rr->regexp = text; rr->replacement = extract_name(&s, "replacement"); if (!rr->replacement) return NULL; if (*s) { return bitch("garbage after valid NAPTR data"); } return store_record(type, name, ttl, rr); }
static int drop_privs_r(const char *username,const char *groupname,size_t buflen){ struct passwd pwent,*pdb = NULL; struct group grent,*grp = NULL; uid_t uid,olduid = getuid(); gid_t gid,oldgid = getgid(); char buf[buflen]; if(groupname){ if(getgrnam_r(groupname,&grent,buf,sizeof(buf),&grp) || !grp){ moan("Couldn't look up group %s\n",groupname); return -1; } gid = grp->gr_gid; if(setgid(gid)){ moan("Couldn't setgid to group %d\n",gid); return -1; } if(getgid() != gid){ bitch("getgid() returned %d, not %d\n",getgid(),gid); return -1; } if(getegid() != gid){ bitch("getegid() returned %d, not %d\n",getegid(),gid); return -1; } nag("Dropped permissions to group %s (GID %d) from GID %d\n",groupname,gid,oldgid); } if(username){ if(getpwnam_r(username,&pwent,buf,sizeof(buf),&pdb) || !pdb){ moan("Couldn't look up username %s\n",username); return -1; } uid = pdb->pw_uid; if(setuid(uid)){ moan("Couldn't setuid to user %d\n",uid); return -1; } if(getuid() != uid){ bitch("getuid() returned %d, not %d\n",getuid(),uid); return -1; } if(geteuid() != uid){ bitch("geteuid() returned %d, not %d\n",geteuid(),uid); return -1; } nag("Dropped permissions to user %s (UID %d) from UID %d\n",username,uid,olduid); } return 0; }
static int extract_certificate_type(char **s, char *what) { int type; char *str_type; if (isdigit(**s)) { type = extract_integer(s, what); if (type >= 1 && type <= 8) return type; if (type == 253 || type == 254) return type; if (type >= 65280 && type <= 65534) return type; if (type < 0 || type > 65535) { bitch("bad certificate type %d", type); return -1; } if (type == 0 || type == 255 || type == 65535) { bitch("certificate type %d is reserved by IANA", type); return -1; } bitch("certificate type %d is unassigned", type); return -1; } else { str_type = extract_label(s, what, "temporary"); if (!str_type) return -1; if (strcmp(str_type, "pkix") == 0) return 1; if (strcmp(str_type, "spki") == 0) return 2; if (strcmp(str_type, "pgp") == 0) return 3; if (strcmp(str_type, "ipkix") == 0) return 4; if (strcmp(str_type, "ispki") == 0) return 5; if (strcmp(str_type, "ipgp") == 0) return 6; if (strcmp(str_type, "acpkix") == 0) return 7; if (strcmp(str_type, "iacpkix") == 0) return 8; if (strcmp(str_type, "uri") == 0) return 253; if (strcmp(str_type, "oid") == 0) return 254; bitch("bad certificate type %s", str_type); return -1; } }
static int antimalware_rewrite(icap_state *is,unsigned probability,const char *name){ #define HDRTAG "X-LOG-amww: " char hdr[strlen(HDRTAG) + SCANMAPI_MAX_STRLEN] = HDRTAG; unsigned int eid = 0, pid = 0, uid = 0, gid = 0; SiteInfoType si = SITEINFOTYPE_INITIALIZER; inc_maldetect(); nag("Rewriting based off probability %u name %s\n",probability,name); strcpy(hdr + strlen(HDRTAG),name); if(add_icap_respheader(is,hdr)){ return -1; } if(is->encaps.http.x_sweb_data) { if(extract_reqmod_data(is->encaps.http.x_sweb_data, &si, &eid, &pid, &uid, &gid)) { nag("Error extracting reqmod data\n"); return reqresp_error_msg(is, "Malware detected, but unable to parse policy data\n"); } } else { bitch("No data header\n"); } if(!pid) { return reqresp_error_msg(is, "Malware detected, but no policy data available\n"); } return reqresp_blocked(is, ec_get_block_page(ec, pid), is->encaps.http.rawuri, &si, name); #undef HDRTAG }
static int lex_uint_ashex(const unsigned char **buf,uintmax_t *val,unsigned bits){ const unsigned char *start = *buf,*numstart; uintmax_t v = 0; int ret = 1; // nag("Looking for %u hex bits in %s\n",bits,*buf); skip_whitespace(buf); numstart = *buf; while(isxdigit(**buf)){ if((ret = safely_add_hex(bits,&v,**buf))){ goto err; } ++*buf; } if(numstart == *buf){ goto err; } *val = v; // nag("Wanted %u-bit uint, got [%llu]\n",bits,*val); return 0; err: bitch("Wanted %u-bit uint, got [%s]\n",bits,start); *buf = start; return ret; }
static int pollinbuf_blob_cb(struct pollfd_state *pfd){ pollinbuf *pibuf = get_pfd_icap(pfd)->pibuf; typeof(pibuf->modestate.chunkdata) *chunk; blob_read_res res; uint32_t remains; chunk = &pibuf->modestate.chunkdata; remains = chunk->chunklen - chunk->chunkread; res = read_blob(pfd->pfd.fd,&pibuf->cr,chunk->okey,&chunk->dumpoff,&remains); if(res == BLOB_READ_SYSERR){ bitch("Error while reading %ub for chunk\n",remains); return -1; } chunk->chunkread += chunk->chunklen - chunk->chunkread - remains; if(res == BLOB_READ_NBLOCK){ return 0; } if(chunk->cb(pfd)){ return -1; } // For edge-triggered RX, we want to return 1 until a read() actually // returns -1 + EAGAIN. For level-triggered, return 0 on // BLOB_READ_SUCCESS. We're either that or BLOB_READ_MOREDATA. return 1; }
// Lexing functions return -1 if they could not convert due to system error, 1 // if a parse error occured. Otherwise, they return 0, advance the double // pointer, and store the result. The dpointer and result var are not molested // if the result is invalid. static int lex_uint(const unsigned char **buf,uintmax_t *val,unsigned bits){ const unsigned char *start = *buf,*numstart; uintmax_t v = 0; int ret = 1; // nag("Looking for %u bits in %s\n",bits,*buf); skip_whitespace(buf); numstart = *buf; if(*numstart == '0'){ if(lex_uint_0helper(buf,val,bits)){ goto err; } }else{ while(isdigit(**buf)){ if((ret = safely_add_dec(bits,&v,**buf))){ goto err; } ++*buf; } if(numstart == *buf){ goto err; } *val = v; } // nag("Wanted %u-bit uint, got [%ju]\n",bits,*val); return 0; err: bitch("Wanted %u-bit uint, got [%s]\n",bits,start); *buf = start; return ret; }
void *hex2bin(const char *asciihex, size_t *len) { void *result; *len = strlen(asciihex); if(*len % 2 || !*len) { bitch("Ascii hex string has invalid %zu characters\n",*len); return NULL; } *len /= 2; result = Malloc("bindata", *len); if(!result) { return NULL; } if(!hextoascii(asciihex, result, EOF, *len)) { nag("Error converting hex string\n"); Free(result); return NULL; } return result; }
int add_icap_respheader(icap_state *is,const char *hdr){ const char *cur; size_t len,hlen; char *tmp; nag("Adding ICAP header: %s\n",hdr); for(cur = hdr ; *cur ; ++cur){ if(*cur == 0xd || *cur == 0xa){ bitch("Illegal character (%u) in header %s\n",*cur,hdr); return -1; } } hlen = strlen(hdr); len = hlen + strlen(CRLF) + 1; if(is->respheaders){ len += strlen(is->respheaders); } if((tmp = Realloc("response headers",is->respheaders,len)) == NULL){ return -1; } is->respheaders = tmp; memcpy(tmp + len - hlen - strlen(CRLF) - 1,hdr,hlen); memcpy(tmp + len - strlen(CRLF) - 1,CRLF,strlen(CRLF) + 1); return 0; }
static inline void purge_fd_resulthandler(poller *p,pollfd_state *state,int fd){ if(state->pfd.fd != fd){ bitch("already killed fd %d\n",fd); inc_stateexceptions(); return; } nag("Purging fd %d\n",fd); if(state->timerfd >= 0){ nag("timerfd set: %d\n",state->timerfd); // state->timerfd == fd on fbsd if(del_timeout_from_pollqueue(p,fd)){ inc_stateexceptions(); } } if(state->freefxn){ state->freefxn(state->state); } if(Close(fd)){ inc_stateexceptions(); } --p->pfds_active; memset(state,0,sizeof(*state)); state->timerfd = -1; state->pfd.fd = -1; }
static int create_kqueue(poller *p){ if(p->pfds_available <= 0){ bitch("Poller's pfds weren't initialized\n"); return -1; } p->ksize = p->pfds_available; p->csize = 2 * p->pfds_available; if((p->kv = Malloc("kqueue vector",sizeof(*p->kv) * p->ksize)) == NULL){ return -1; } if((p->cv = Malloc("kchange vector",sizeof(*p->cv) * p->csize)) == NULL){ Free(p->kv); return -1; } if((p->kq = kqueue()) < 0){ Free(p->kv); Free(p->cv); return -1; } p->kchanges = 0; if(enqueue_kqueue_change(p,POLLERSIGNAL,EVFILT_SIGNAL,EV_ADD,0,0,NULL)){ destroy_kqueue(p); return -1; } if(enqueue_kqueue_change(p,SIGCHLD,EVFILT_SIGNAL,EV_ADD,0,0,NULL)){ destroy_kqueue(p); return -1; } return 0; }
int main(int argc, char **argv) { g_fd = open(argv[1], O_RDWR); if (errno == ENOENT && g_fd < 0) { int i; for (i = 0; i < 120; i++) { g_fd = open(argv[1], O_RDWR); if (g_fd >= 0) break; printf("."); fflush(stdout); usleep(500000); } printf("\n"); } if (g_fd < 0) return bitch("open"); // reset(); // set_config(0); // set_config(1); usb_set_connected(0, 1); claim_if(0); #if 0 msd(); #else palm(); #endif return 0; }
static int icapstartlines_bad(const char **slines){ struct sockaddr_in sina; const char **cur; int ret = -1,sd; if(common_start()){ goto done; } memset(&sina,0,sizeof(sina)); sina.sin_port = htons(ICAP_DEFAULT_PORT); sina.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sina.sin_family = AF_INET; for(cur = slines ; *cur ; ++cur){ if((sd = make_tcp_socket(PF_INET)) >= 0){ if(Connect(sd,(struct sockaddr *)&sina,sizeof(sina)) == 0){ printf(" Sending %s",*cur); if((strlen(*cur) == 0 || Writen(sd,*cur,strlen(*cur)) == 0)){ int i,v = 0; char c; while((i = read(sd,&c,sizeof(c))) == sizeof(c)){ if(!v){ printf(" "); v = 1; } putc(c,stdout); if(c == '\n'){ v = 0; } } if(i == 0){ if(Close(sd)){ goto done; } continue; // mmm, spaghetti }else{ if(i < 0){ moan("Couldn't read reply on %d\n",sd); }else{ bitch("Got %d reading on %d\n",i,sd); } } } } Close(sd); } goto done; } ret = 0; done: ret |= close_icap_servers(); ret |= reap_poller_thread(snarepoller); ret |= destroy_poller(snarepoller); ret |= stop_config(); ret |= stop_fileconf(); return ret; }
static int parse_icap_version(char **buf){ parse_whitespaces(buf); if(strncasecmp(*buf,ICAP_VERSION_STR,__builtin_strlen(ICAP_VERSION_STR))){ bitch("Bad ICAP version: %s != %s\n",*buf,ICAP_VERSION_STR); return -1; } *buf += __builtin_strlen(ICAP_VERSION_STR); return 0; }
static int open_procfs_file(const char *relpath){ char path[PATH_MAX]; if(snprintf(path,PATH_MAX,PROCMOUNT"/%s",relpath) >= PATH_MAX){ bitch("Pathname was too long: "PROCMOUNT"/%s\n",relpath); return -1; } return Open(path,O_RDONLY); }
static struct rr* cert_parse(char *name, long ttl, int type, char *s) { struct rr_cert *rr = getmem(sizeof(*rr)); int cert_type, key_tag, alg; cert_type = extract_certificate_type(&s, "certificate type"); if (cert_type < 0) return NULL; rr->type = cert_type; key_tag = extract_integer(&s, "key tag"); if (key_tag < 0) return NULL; if (key_tag > 65535) return bitch("bad key tag"); rr->key_tag = key_tag; if (isdigit(*s)) { alg = extract_integer(&s, "algorithm"); if (alg < 0) return NULL; if (alg > 255) return bitch("bad algorithm"); if (alg != 0) { /* 0 is just fine */ if (algorithm_type(alg) == ALG_UNSUPPORTED) return bitch("bad algorithm %d", alg); } } else { alg = extract_algorithm(&s, "algorithm"); if (alg == ALG_UNSUPPORTED) return NULL; } rr->algorithm = alg; if (alg == 0 && key_tag != 0) { /* we might want to bitch here, but RFC says "SHOULD", so we don't */ } rr->certificate = extract_base64_binary_data(&s, "certificate"); if (rr->certificate.length < 0) return NULL; /* TODO validate cert length based on algorithm */ if (*s) { return bitch("garbage after valid CERT data"); } return store_record(type, name, ttl, rr); }
int add_signal_to_evcore(evhandler *eh,struct evectors *ev,int sig, evcbfxn rfxn,void *cbstate){ if(sig >= eh->sigarraysize){ bitch("Signal too high (%d >= %u)\n",sig,eh->sigarraysize); return -1; } if(add_signal_event(eh,ev,sig,rfxn,cbstate)){ return -1; } return 0; }
int writeq_sendfile(writeq *wq,struct oqueue_key *okey,off_t off,off_t len){ pending_msg *pm; if(len < 0){ bitch("Cannot enqueue %lldb file @%lld\n",(long long)len,(long long)off); return -1; }else if(len == 0){ return 0; }else if(sizeof(len) > sizeof(size_t)){ if(len > (typeof(len))SSIZE_MAX){ bitch("%lld > %lld\n",(long long)len,(long long)(typeof(len))SSIZE_MAX); return -1; } } if((pm = create_pending_msg_okey(okey,off,(size_t)len)) == NULL){ return -1; } enqueue_writer_msg(wq,pm); return 0; }
// Move the map back to the beginning of the underlying object (slide it left) int reset_mmap_window(mmap_window *mw,int fd,int prot){ void *tmp; if(mw->maplen <= 0){ bitch("Invalid arguments (%zu)\n",mw->maplen); return -1; } if(fd >= 0){ tmp = Mmap(mw->mapbase,mw->maplen,prot,mmap_flags(fd) | MAP_FIXED,fd,0); if(tmp != mw->mapbase){ bitch("Invalid MAP_FIXED result (%p, %p)\n",tmp,mw->mapbase); if(tmp != MAP_FAILED){ // FIXME verify it doesn't overlap! Munmap(tmp,mw->maplen); } return -1; } } mw->mapoff = 0; return 0; }
static struct rr *afsdb_parse(char *name, long ttl, int type, char *s) { struct rr_afsdb *rr = getmem(sizeof(*rr)); rr->subtype = extract_integer(&s, "AFSDB subtype", NULL); if (rr->subtype < 0) return NULL; if (rr->subtype != 1 && rr->subtype != 2) return bitch("unknown AFSDB subtype"); rr->hostname = extract_name(&s, "AFSDB hostname", 0); if (!rr->hostname) return NULL; if (*s) { return bitch("garbage after valid AFSDB data"); } return store_record(type, name, ttl, rr); }
char *Strdup(const char *s){ char *ret; if(s == NULL){ bitch("Can't duplicate NULL\n"); return NULL; } if( (ret = Malloc("string copy",strlen(s) + 1)) ){ strcpy(ret,s); } return ret; }
static int handle_kqueue_timeout(struct poller *p,pollfd_state *state){ typeof(state->timeoutfxn) tout = state->timeoutfxn; int ret = 0; if(tout == NULL){ bitch("No timeout callback for POLLOUT on %d\n",state->pfd.fd); }else{ ret |= tout(p,state); } return ret; }
static struct rr *a_parse(char *name, long ttl, int type, char *s) { struct rr_a *rr = getmem(sizeof(*rr)); if (extract_ipv4(&s, "IPv4 address", &rr->address) <= 0) return NULL; if (*s) { return bitch("garbage after valid A data"); } return store_record(type, name, ttl, rr); }
// Slide the map forward over the mapped object int slide_mmap_window(mmap_window *mw,int fd,int prot,size_t delta){ void *tmp; if(delta == 0 || mw->maplen <= 0){ bitch("Invalid arguments (%zu, %zu)\n",delta,mw->maplen); return -1; } // FIXME ought be using remap_file_pages(2) on Linux for performance tmp = Mmap(mw->mapbase,mw->maplen,prot,mmap_flags(fd) | MAP_FIXED, fd,mw->mapoff + delta); if(tmp != mw->mapbase){ bitch("Invalid MAP_FIXED result (%p, %p)\n",tmp,mw->mapbase); if(tmp != MAP_FAILED){ Munmap(tmp,mw->maplen); // FIXME unsafe } track_failloc(); return -1; } mw->mapoff += delta; return 0; }