void* nsec3_validate(struct rr *rrv) { RRCAST(nsec3); if (!first_nsec3) { first_nsec3 = rr; } if (latest_nsec3) { if (memcmp(latest_nsec3->next_hashed_owner.data, rr->this_hashed_name.data, 20) != 0) { char *expected_name = quickstrdup_temp(rr->rr.rr_set->named_rr->name); /* guaranteed to have same length, I think */ encode_base32hex(expected_name, 32, latest_nsec3->next_hashed_owner.data, 20); if (rr == first_nsec3) { moan(latest_nsec3->rr.file_name, latest_nsec3->rr.line, "broken NSEC3 chain, expected %s, but nothing found", expected_name); } else { moan(latest_nsec3->rr.file_name, latest_nsec3->rr.line, "broken NSEC3 chain, expected %s, but found %s", expected_name, rr->rr.rr_set->named_rr->name); } if (rr != first_nsec3) latest_nsec3->next_nsec3 = rr; latest_nsec3 = rr; return NULL; } if (rr != first_nsec3) latest_nsec3->next_nsec3 = rr; } latest_nsec3 = rr; return rr; }
static void *dnskey_validate(struct rr *rrv) { RRCAST(dnskey); if (G.opt.policy_checks[POLICY_DNSKEY]) { if (algorithm_type(rr->algorithm) == ALG_RSA_FAMILY) { unsigned int e_bytes; unsigned char *pk; int l; pk = (unsigned char *)rr->pubkey.data; l = rr->pubkey.length; e_bytes = *pk++; l--; if (e_bytes == 0) { if (l < 2) return moan(rr->rr.file_name, rr->rr.line, "public key is too short"); e_bytes = (*pk++) << 8; e_bytes += *pk++; l -= 2; } if (l < e_bytes) return moan(rr->rr.file_name, rr->rr.line, "public key is too short"); if (*pk == 0) return moan(rr->rr.file_name, rr->rr.line, "leading zero octets in public key exponent"); pk += e_bytes; l -= e_bytes; if (l > 0 && *pk == 0) return moan(rr->rr.file_name, rr->rr.line, "leading zero octets in key modulus"); } } return NULL; }
static void *soa_validate(struct rr *rrv) { RRCAST(soa); if (strchr(rr->mname, '/') != NULL) return moan(rr->rr.file_name, rr->rr.line, "MNAME contains '/'"); if (strchr(rr->rname, '/') != NULL) return moan(rr->rr.file_name, rr->rr.line, "RNAME contains '/'"); return NULL; }
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 void* tlsa_validate_set(struct rr_set *rr_set) { struct rr *rr; struct named_rr *named_rr; char *s; int port = 0; int len; if (G.opt.policy_checks[POLICY_TLSA_HOST]) { rr = rr_set->tail; named_rr = rr_set->named_rr; /* _25._tcp.mail.example.com. */ s = named_rr->name; if (*s != '_') { not_a_prefixed_domain_name: return moan(rr->file_name, rr->line, "not a proper prefixed DNS domain name"); } s++; while (isdigit(*s)) { port = port * 10 + *s - '0'; s++; } if (port <= 0 || port > 65535) goto not_a_prefixed_domain_name; if (*s++ != '.') goto not_a_prefixed_domain_name; len = strlen(s); if (len < 6) goto not_a_prefixed_domain_name; if (memcmp(s, "_tcp.", 5) != 0 && memcmp(s, "_udp.", 5) != 0 && memcmp(s, "_sctp.", 6) != 0) goto not_a_prefixed_domain_name; } return NULL; }
static void* smimea_validate_set(struct rr_set *rr_set) { struct rr *rr; struct named_rr *named_rr; char *s; int hash_len = 0; int len; if (G.opt.policy_checks[POLICY_SMIMEA_HOST]) { rr = rr_set->tail; named_rr = rr_set->named_rr; /* c93f1e400f26708f98cb19d936620da35eec8f72e57f9eec01c1afd6._smimecert.example.com. */ s = named_rr->name; while (isxdigit(*s)) { hash_len++; s++; } if (*s++ != '.' || hash_len != 56) { not_a_proper_smimea_domainname: return moan(rr->file_name, rr->line, "not a proper domain name for an SMIMEA record"); } len = strlen(s); if (len < 11) goto not_a_proper_smimea_domainname; if (memcmp(s, "_smimecert.", 11) != 0) goto not_a_proper_smimea_domainname; } return NULL; }
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 void* aaaa_validate_set(struct rr_set *rr_set) { if (rr_set->named_rr->flags & NAME_FLAG_CONTAINS_SLASH) { struct rr *rr = rr_set->tail; return moan(rr->file_name, rr->line, "host name contains '/'"); } return NULL; }
static void truncate_crash_log_locked(int exit_status){ int fd = -1; logctx *lc; if((lc = get_thread_logctx()) == NULL){ goto done; } if(!crash_log){ goto done; } if(avail_crash_log){ timenag("Closing unused crash log\n"); avail_crash_log = NULL; if(fclose(crash_log) == EOF){ moan("Couldn't close crash log at %p\n",crash_log); } Unlink(crash_fn); goto done; } if(lc->lfile != crash_log){ bitch("Crash log %p claimed, cur = %p\n",crash_log,lc->lfile); goto done; } if((fd = fileno(crash_log)) < 0){ moan("Couldn't extract fd from crash log %p\n",crash_log); goto done; } nag("Truncating crash log at fd %d\n",fd); timenag("Halting! Exit code: %d\n",exit_status); Ftruncate(fd,lc->lfile_offset); // closing crash_log here in fatal sig handler context -> lockup, thus: lc->lfile = NULL; // FIXME done: if(fd < 0){ timenag("Halting with exit code %d\n",exit_status); } crash_log = NULL; free_logctx(lc); free(logdir); logdir = NULL; return; }
int dnskey_build_pkey(struct rr_dnskey *rr) { if (rr->pkey_built) return rr->pkey ? 1 : 0; rr->pkey_built = 1; if (algorithm_type(rr->algorithm) == ALG_RSA_FAMILY) { RSA *rsa; EVP_PKEY *pkey; unsigned int e_bytes; unsigned char *pk; int l; rsa = RSA_new(); if (!rsa) goto done; pk = (unsigned char *)rr->pubkey.data; l = rr->pubkey.length; e_bytes = *pk++; l--; if (e_bytes == 0) { if (l < 2) /* public key is too short */ goto done; e_bytes = (*pk++) << 8; e_bytes += *pk++; l -= 2; } if (l < e_bytes) /* public key is too short */ goto done; rsa->e = BN_bin2bn(pk, e_bytes, NULL); pk += e_bytes; l -= e_bytes; rsa->n = BN_bin2bn(pk, l, NULL); pkey = EVP_PKEY_new(); if (!pkey) goto done; if (!EVP_PKEY_set1_RSA(pkey, rsa)) goto done; rr->pkey = pkey; } done: if (!rr->pkey) { moan(rr->rr.file_name, rr->rr.line, "error building pkey"); } return rr->pkey ? 1 : 0; }
static void *mx_validate(struct rr *rrv) { RRCAST(mx); if (G.opt.policy_checks[POLICY_MX_ALIAS]) { if (find_rr_set(T_CNAME, rr->exchange)) { return moan(rr->rr.file_name, rr->rr.line, "MX exchange is an alias"); } } return NULL; }
// Parameters are taken from epoll_ctl (see epoll_ctl(2)) static int enqueue_epoll_change(poller *p,int fd,int op,uint32_t events){ struct epoll_event ev; memset(&ev,0,sizeof(ev)); ev.events = events; ev.data.fd = fd; if(epoll_ctl(p->epfd,op,fd,&ev)){ moan("Couldn't register %d with epoll\n",fd); return -1; } return 0; }
void dnskey_ksk_policy_check(void) { struct rr_dnskey *rr = all_dns_keys; int ksk_found = 0; while (rr) { if (rr->key_type == KEY_TYPE_KSK) ksk_found = 1; rr = rr->next_key; } if (!ksk_found) moan(all_dns_keys->rr.file_name, all_dns_keys->rr.line, "No KSK found"); }
static void signalfd_demultiplexer(int fd,void *cbstate){ struct signalfd_siginfo si; evhandler *e = cbstate; ssize_t r; do{ if((r = read(fd,&si,sizeof(si))) == sizeof(si)){ handle_evsource_read(e->sigarray,si.ssi_signo); }else if(r >= 0){ bitch("Got short read (%zd) off signalfd %d\n",r,fd); // FIXME stat! } }while(r >= 0 && errno != EINTR); if(errno != EAGAIN){ moan("Error reading from signalfd %d\n",fd); } }
static writeq_res txstr(int sd,pending_msg *pm){ ssize_t ret; do{ // precondition: amount to send > 0 // loop around possible EINTRs if((ret = write(sd,pm->content.u.string + pm->off,pm->tosend)) < 0){ if(errno == EAGAIN || errno == EWOULDBLOCK){ return WRITEQ_RES_NBLOCK; }else if(errno != EINTR){ // loop on eintr moan("Error writing %zud on %d\n",pm->tosend,sd); return WRITEQ_RES_SYSERR; } }else{ pm->off += ret; pm->tosend -= ret; } }while(pm->tosend); return WRITEQ_RES_SUCCESS; }
// Suitable for edge-triggered event handling without signal-driven control // flows (we arbitrarily loop on EINTR). sendfile(2) is used for file-backed // maps, write(2) otherwise, but fallback from sendfile(2) errors to write(2) // is not implemented. static int txfile(int sd,pending_msg *pm){ ssize_t ret; do{ // precondition: amount to send > 0 off_t oldoff = pm->off; /*if(pm->content.okey->fd >= 0){ nag("%zu SENDFILE from %d to %d\n",pm->tosend,pm->content.okey->fd,sd); ret = sendfile_compat(sd,pm->content.okey->fd,&pm->off,pm->tosend); }else{*/ //nag("%zu WRITE from %p to %d\n",pm->tosend,pm->content.okey->buf,sd); ret = write(sd,oqueue_const_ptrto(pm->content.okey,pm->off), pm->tosend); if(ret >= 0){ pm->off += ret; } // } pm->tosend -= pm->off - oldoff; if(ret < 0){ if(errno == EAGAIN || errno == EWOULDBLOCK){ return WRITEQ_RES_NBLOCK; }else if(errno != EINTR){ // loop on EINTR moan("Error during %jd@%zu tx on %d\n",(intmax_t)pm->off,pm->tosend,sd); return WRITEQ_RES_SYSERR; } } }while(pm->tosend); if(pm->content.okey->cbarg){ if(icap_state_verdictp(get_const_pfd_state(pm->content.okey->cbarg))){ if(window_icap_encapsulate(pm->content.okey,pm->off)){ return WRITEQ_RES_SYSERR; } } } return WRITEQ_RES_SUCCESS; }
static blob_read_res read_blob(int sd,crlf_reader *br,oqueue_key *okey,size_t *bufoff,uint32_t *remains){ size_t tosend,toread; ssize_t readnow; if(*remains == 0){ bitch("Asked to read 0 bytes\n"); return BLOB_READ_SYSERR; } // nag("Want %ju at %p:%zu from %d\n",(uintmax_t)*remains,buf,*bufoff,sd); // See if we can handle the request with what was left over if(br->count){ tosend = br->count; if(tosend > *remains){ tosend = *remains; } if(okey){ memcpy(oqueue_ptrto(okey,*bufoff),br->buf,tosend); } *bufoff += tosend; if( (br->count -= tosend) ){ memmove(br->buf,br->buf + tosend,br->count); } if((*remains -= tosend) == 0){ return (br->count || br->eof || br->readreq) ? BLOB_READ_MOREDATA : BLOB_READ_SUCCESS; } } // We need more data. If we previously saw eof, we can't get it... if(br->eof){ return BLOB_READ_SYSERR; } br->readreq = 0; while(br->total < *remains){ // loop on a possible EINTR while((readnow = read(sd,br->buf,br->total)) < 0){ if(errno == EAGAIN || errno == EWOULDBLOCK){ return BLOB_READ_NBLOCK; }else if(errno != EINTR){ moan("Error reading %zu from %d\n",br->total,sd); return BLOB_READ_SYSERR; } } if(readnow == 0){ bitch("Got EOF, still wanted %u\n",*remains); return BLOB_READ_SYSERR; } tosend = readnow; if(okey){ memcpy(oqueue_ptrto(okey,*bufoff),br->buf,readnow); } *bufoff += readnow; *remains -= readnow; } toread = *remains; // loop on a possible EINTR while((readnow = read(sd,br->buf,toread)) < 0){ if(errno == EAGAIN || errno == EWOULDBLOCK){ return BLOB_READ_NBLOCK; }else if(errno != EINTR){ moan("Error reading %zu from %d\n",toread,sd); return BLOB_READ_SYSERR; } } if(readnow == 0){ bitch("Got EOF, still wanted %zu\n",toread); return BLOB_READ_SYSERR; } tosend = readnow; if(okey){ memcpy(oqueue_ptrto(okey,*bufoff),br->buf,tosend); } *bufoff += tosend; if((*remains -= readnow) == 0){ br->readreq = 1; return BLOB_READ_MOREDATA; } return BLOB_READ_NBLOCK; }
int dnskey_build_pkey(struct rr_dnskey *rr) { if (rr->pkey_built) return rr->pkey ? 1 : 0; rr->pkey_built = 1; if (algorithm_type(rr->algorithm) == ALG_RSA_FAMILY) { RSA *rsa; EVP_PKEY *pkey; unsigned int e_bytes; unsigned char *pk; int l; rsa = RSA_new(); if (!rsa) goto done; pk = (unsigned char *)rr->pubkey.data; l = rr->pubkey.length; e_bytes = *pk++; l--; if (e_bytes == 0) { if (l < 2) /* public key is too short */ goto done; e_bytes = (*pk++) << 8; e_bytes += *pk++; l -= 2; } if (l < e_bytes) /* public key is too short */ goto done; rsa->e = BN_bin2bn(pk, e_bytes, NULL); pk += e_bytes; l -= e_bytes; rsa->n = BN_bin2bn(pk, l, NULL); pkey = EVP_PKEY_new(); if (!pkey) goto done; if (!EVP_PKEY_set1_RSA(pkey, rsa)) goto done; rr->pkey = pkey; } else if (algorithm_type(rr->algorithm) == ALG_ECC_FAMILY) { EC_KEY *pubeckey; EVP_PKEY *pkey; unsigned char *pk; int l; BIGNUM *bn_x = NULL; BIGNUM *bn_y = NULL; if (rr->algorithm == ALG_ECDSAP256SHA256) { l = SHA256_DIGEST_LENGTH; pubeckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); } else if (rr->algorithm == ALG_ECDSAP384SHA384) { l = SHA384_DIGEST_LENGTH; pubeckey = EC_KEY_new_by_curve_name(NID_secp384r1); } else { goto done; } if (!pubeckey) goto done; if (rr->pubkey.length != 2*l) { goto done; } pk = (unsigned char *)rr->pubkey.data; bn_x = BN_bin2bn(pk, l, NULL); bn_y = BN_bin2bn(&pk[l], l, NULL); if (1 != EC_KEY_set_public_key_affine_coordinates(pubeckey, bn_x, bn_y)) { goto done; } pkey = EVP_PKEY_new(); if (!pkey) goto done; if (!EVP_PKEY_assign_EC_KEY(pkey, pubeckey)) goto done; rr->pkey = pkey; } done: if (!rr->pkey) { moan(rr->rr.file_name, rr->rr.line, "error building pkey"); } return rr->pkey ? 1 : 0; }
// Heap allocates and returns the next line, as delimited by '\n'. Caller must // free() this result. Comments (lines beginning with optional whitespace and // a '#' character) are stripped. Returns NULL on end of file or I/O error; // check errno to differentiate. Errno of 0 indicates standard EOF (success). char *line_parser_next(line_parser_ctx *ctx){ unsigned examined = 0; while(ctx->count > examined || !ctx->eof){ char *end; // if we've examined all that we've read, we need to read more! // if we can't read anymore, return what we have! if(ctx->count == examined){ size_t tlen,uret; ssize_t ret; if(ctx->count == ctx->total - 1){ char *tmp; if((tmp = Realloc("line parser buf",ctx->buf,ctx->total + BUFSIZ)) == NULL){ return NULL; } ctx->total += BUFSIZ; ctx->buf = tmp; }else if(ctx->count > ctx->base){ memmove(ctx->buf,ctx->buf + ctx->base,ctx->count); }else if(ctx->count){ memcpy(ctx->buf,ctx->buf + ctx->base,ctx->count); } ctx->base = 0; tlen = ctx->total - ctx->count - 1; ret = read(ctx->fd,ctx->buf + ctx->count,tlen); if(ret < 0){ ctx->eof = 1; moan("Couldn't read %zu from %d\n",tlen,ctx->fd); return NULL; }else if((uret = ret) < tlen){ ctx->eof = 1; if(uret == 0){ break; } } ctx->count += uret; ctx->buf[ctx->count] = '\0'; } // we must have something (empty read broke out) if(examined == 0){ int joinlines = 0,joinc = 0,joinb = 0; while(ctx->count){ if(isspace(ctx->buf[ctx->base])){ if(ctx->buf[ctx->base] == '\n'){ if(joinlines){ joinlines = 0; ctx->buf[joinb] = ' '; ctx->buf[ctx->base] = ' '; } } --ctx->count; ++ctx->base; }else if(ctx->buf[ctx->base] == '\\'){ if(joinlines){ ctx->count = joinc; ctx->base = joinb; break; }else{ joinlines = 1; joinc = ctx->count; joinb = ctx->base; } }else{ if(joinlines){ ctx->count = joinc; ctx->base = joinb; } break; } } if(ctx->count == 0){ continue; } } // still must have something (whitespace broke out) while( (end = strchr(ctx->buf + ctx->base + examined,'\n')) ){ // safe; if '\n' was first, previous loop got it char *tmp = end; do{ --tmp; if(*tmp == '\\'){ *tmp = ' '; *end = ' '; examined += end - (ctx->buf + ctx->base + examined); end = NULL; break; }else if(!isspace(*tmp)){ break; } }while(tmp != ctx->buf + ctx->base); if(end){ break; } } if(end){ if(is_comment(ctx->buf,ctx->base)){ ctx->count -= (end - (ctx->buf + ctx->base) + 1); ctx->base += (end - (ctx->buf + ctx->base) + 1); examined = 0; }else{ char *tmp = ctx->buf + ctx->base; ctx->count -= (end - (ctx->buf + ctx->base) + 1); ctx->base += (end - (ctx->buf + ctx->base) + 1); *end = '\0'; return tmp; } }else{ examined = ctx->count; } } // we have no data left to read, and it has no \n's; return what we have if(ctx->count && !is_comment(ctx->buf,ctx->base)){ ctx->count = 0; return ctx->buf + ctx->base; } errno = 0; return NULL; }