char *tls_fingerprint(X509 *peercert, const char *dgst) { const char *myname = "tls_fingerprint"; const EVP_MD *md_alg; unsigned char md_buf[EVP_MAX_MD_SIZE]; unsigned int md_len; int i; char *result = 0; /* Previously available in "init" routine. */ if ((md_alg = EVP_get_digestbyname(dgst)) == 0) acl_msg_panic("%s: digest algorithm \"%s\" not found", myname, dgst); /* Fails when serialization to ASN.1 runs out of memory */ if (X509_digest(peercert, md_alg, md_buf, &md_len) == 0) acl_msg_fatal("%s: error computing certificate %s digest (out of memory?)", myname, dgst); /* Check for OpenSSL contract violation */ if (md_len > EVP_MAX_MD_SIZE || (int) md_len >= INT_MAX / 3) acl_msg_panic("%s: unexpectedly large %s digest size: %u", myname, dgst, md_len); result = acl_mymalloc(md_len * 3); for (i = 0; i < (int) md_len; i++) { result[i * 3] = hexcodes[(md_buf[i] & 0xf0) >> 4U]; result[(i * 3) + 1] = hexcodes[(md_buf[i] & 0x0f)]; result[(i * 3) + 2] = (i + 1 != (int) md_len) ? ':' : '\0'; } return (result); }
static int new_client_session_cb(SSL *ssl, SSL_SESSION *session) { const char *myname = "new_client_session_cb"; TLS_SESS_STATE *TLScontext; ACL_VSTRING *session_data; /* * The cache name (if caching is enabled in tlsmgr(8)) and the cache ID * string for this session are stored in the TLScontext. It cannot be * null at this point. */ if ((TLScontext = SSL_get_ex_data(ssl, TLScontext_index)) == 0) acl_msg_panic("%s: null TLScontext in new session callback", myname); /* * We only get here if the cache_type is not empty. This callback is not * set unless caching is enabled and the cache_type is stored in the * server SSL context. */ if (TLScontext->cache_type == 0) acl_msg_panic("%s: null session cache type in new session callback", myname); if (TLScontext->log_level >= 2) acl_msg_info("save session %s to %s cache", TLScontext->serverid, TLScontext->cache_type); #if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L) /* * Ugly Hack: OpenSSL before 0.9.6a does not store the verify result in * sessions for the client side. We modify the session directly which is * version specific, but this bug is version specific, too. * * READ: 0-09-06-01-1 = 0-9-6-a-beta1: all versions before beta1 have this * bug, it has been fixed during development of 0.9.6a. The development * version of 0.9.7 can have this bug, too. It has been fixed on * 2000/11/29. */ session->verify_result = SSL_get_verify_result(TLScontext->con); #endif /* * Passivate and save the session object. Errors are non-fatal, since * caching is only an optimization. */ if ((session_data = tls_session_passivate(session)) != 0) { tls_mgr_update(TLScontext->cache_type, TLScontext->serverid, STR(session_data), (int) LEN(session_data)); acl_vstring_free(session_data); } /* * Clean up. */ SSL_SESSION_free(session); /* 200502 */ return (1); }
ACL_WATCHDOG *acl_watchdog_create(unsigned timeout, ACL_WATCHDOG_FN action, char *context) { const char* myname = "acl_watchdog_create"; struct sigaction sig_action; ACL_WATCHDOG *wp; wp = (ACL_WATCHDOG *) acl_mymalloc(sizeof(*wp)); if ((wp->timeout = timeout / ACL_WATCHDOG_STEPS) == 0) acl_msg_panic("%s: timeout %d too small", myname, timeout); wp->action = action; wp->context = context; wp->saved_watchdog = acl_watchdog_curr; wp->saved_time = alarm(0); sigemptyset(&sig_action.sa_mask); #ifdef SA_RESTART sig_action.sa_flags = SA_RESTART; #else sig_action.sa_flags = 0; #endif sig_action.sa_handler = acl_watchdog_event; if (sigaction(SIGALRM, &sig_action, &wp->saved_action) < 0) acl_msg_fatal("%s: sigaction(SIGALRM): %s", myname, acl_last_serror()); if (acl_msg_verbose > 1) acl_msg_info("%s: %p %d", myname, (void *) wp, timeout); return (acl_watchdog_curr = wp); }
void acl_master_status_init(ACL_MASTER_SERV *serv) { const char *myname = "acl_master_status_init"; /* * Sanity checks. */ if (serv->status_fd[0] >= 0 || serv->status_fd[1] >= 0) acl_msg_panic("%s: status events already enabled", myname); if (acl_msg_verbose) acl_msg_info("%s: %s", myname, serv->name); /* * Make the read end of this service's status pipe non-blocking so that * we can detect partial writes on the child side. We use a duplex pipe * so that the child side becomes readable when the master goes away. */ if (acl_duplex_pipe(serv->status_fd) < 0) acl_msg_fatal("pipe: %s", strerror(errno)); acl_non_blocking(serv->status_fd[0], ACL_BLOCKING); acl_close_on_exec(serv->status_fd[0], ACL_CLOSE_ON_EXEC); acl_close_on_exec(serv->status_fd[1], ACL_CLOSE_ON_EXEC); serv->status_read_stream = acl_vstream_fdopen(serv->status_fd[0], O_RDWR, acl_var_master_buf_size, acl_var_master_rw_timeout, ACL_VSTREAM_TYPE_SOCK); if (acl_msg_verbose) acl_msg_info("%s(%d)->%s: call acl_event_enable_read, " "status_fd = %d", __FILE__, __LINE__, myname, serv->status_fd[0]); acl_event_enable_read(acl_var_master_global_event, serv->status_read_stream, 0, master_status_event, (void *) serv); }
void tls_set_dh_from_file(const char *path, int bits) { const char *myname = "tls_set_dh_from_file"; FILE *paramfile; DH **dhPtr = 0; switch (bits) { case 512: dhPtr = &dh_512; break; case 1024: dhPtr = &dh_1024; break; default: acl_msg_panic("Invalid DH parameters size %d, file %s", bits, path); } if (*dhPtr != 0) return; if ((paramfile = fopen(path, "r")) != 0) { if ((*dhPtr = PEM_read_DHparams(paramfile, 0, 0, 0)) == 0) { acl_msg_warn("%s: cannot load %d-bit DH parameters from file %s" " -- using compiled-in defaults", myname, bits, path); tls_print_errors(); } (void) fclose(paramfile); /* 200411 */ } else { acl_msg_warn("%s: cannot load %d-bit DH parameters from file %s: %s" " -- using compiled-in defaults", myname, bits, path, acl_last_serror()); } }
void acl_mempool_ctl(int name, ...) { const char *myname = "acl_mempool_ctl"; va_list ap; int n; if (__var_allocator == NULL) acl_msg_fatal("%s(%d): call acl_mempool_create first", myname, __LINE__); va_start(ap, name); for (; name != ACL_MEMPOOL_CTL_END; name = va_arg(ap, int)) { switch (name) { case ACL_MEMPOOL_CTL_MUTEX: n = va_arg(ap, int); if (n) mempool_init_with_mutex(); else mempool_init_no_mutex(); break; case ACL_MEMPOOL_CTL_DISABLE: n = va_arg(ap, int); if (n) mempool_close(); break; default: acl_msg_panic("%s: bad name %d", myname, name); } } va_end(ap); }
void acl_master_status_cleanup(ACL_MASTER_SERV *serv) { const char *myname = "acl_master_status_cleanup"; /* * Sanity checks. */ if (serv->status_fd[0] < 0 || serv->status_fd[1] < 0) acl_msg_panic("%s: status events not enabled", myname); if (acl_msg_verbose) acl_msg_info("%s: %s", myname, serv->name); /* * Dispose of this service's status pipe after disabling read events. */ acl_event_disable_readwrite(acl_var_master_global_event, serv->status_read_stream); if (close(serv->status_fd[0]) != 0) acl_msg_warn("%s: close status descriptor (read side): %s", myname, strerror(errno)); if (close(serv->status_fd[1]) != 0) acl_msg_warn("%s: close status descriptor (write side): %s", myname, strerror(errno)); serv->status_fd[0] = serv->status_fd[1] = -1; if (serv->status_read_stream) acl_vstream_free(serv->status_read_stream); serv->status_read_stream = NULL; }
ssize_t tls_prng_dev_read(TLS_PRNG_SRC *dev, size_t len) { const char *myname = "tls_prng_dev_read"; unsigned char buffer[UCHAR_MAX]; ssize_t count; size_t rand_bytes; if (len <= 0) acl_msg_panic("%s: bad read length: %ld", myname, (long) len); if (len > sizeof(buffer)) rand_bytes = sizeof(buffer); else rand_bytes = len; errno = 0; #ifdef ACL_UNIX count = acl_timed_read(dev->fd.file, buffer, (int) rand_bytes, dev->timeout, NULL); #elif defined(WIN32) count = acl_file_read(dev->fd.file, buffer, (int) rand_bytes, dev->timeout, NULL); #endif if (count > 0) { if (acl_msg_verbose) acl_msg_info("%s: read %ld bytes from entropy device %s", myname, (long) count, dev->name); RAND_seed(buffer, count); } else { if (acl_msg_verbose) acl_msg_info("%s: cannot read %ld bytes from entropy device %s: %s", myname, (long) rand_bytes, dev->name, acl_last_serror()); } return (count); }
static void event_enable_write(ACL_EVENT *eventp, ACL_VSTREAM *stream, int timeout, ACL_EVENT_NOTIFY_RDWR callback, void *context) { const char *myname = "event_enable_write"; EVENT_POLL_THR *event_thr = (EVENT_POLL_THR *) eventp; ACL_EVENT_FDTABLE *fdp; ACL_SOCKET sockfd; sockfd = ACL_VSTREAM_SOCK(stream); fdp = (ACL_EVENT_FDTABLE*) stream->fdp; if (fdp == NULL) { fdp = event_fdtable_alloc(); fdp->listener = 0; fdp->stream = stream; stream->fdp = (void *) fdp; } else if (fdp->flag & EVENT_FDTABLE_FLAG_READ) acl_msg_panic("%s(%d)->%s: fd %d: multiple I/O request", __FILE__, __LINE__, myname, sockfd); else { fdp->listener = 0; fdp->stream = stream; } if (fdp->w_callback != callback || fdp->w_context != context) { fdp->w_callback = callback; fdp->w_context = context; } if (timeout > 0) { fdp->w_timeout = timeout * 1000000; fdp->w_ttl = eventp->present + fdp->w_timeout; } else { fdp->w_ttl = 0; fdp->w_timeout = 0; } if ((fdp->flag & EVENT_FDTABLE_FLAG_WRITE) != 0) return; stream->nrefer++; fdp->flag = EVENT_FDTABLE_FLAG_WRITE | EVENT_FDTABLE_FLAG_EXPT; THREAD_LOCK(&event_thr->event.tb_mutex); fdp->fdidx = eventp->fdcnt; eventp->fdtabs[eventp->fdcnt++] = fdp; event_thr->fds[fdp->fdidx].fd = sockfd; event_thr->fds[fdp->fdidx].events = POLLOUT | POLLHUP | POLLERR; if (eventp->maxfd == ACL_SOCKET_INVALID || eventp->maxfd < sockfd) eventp->maxfd = sockfd; acl_fdmap_add(event_thr->fdmap, sockfd, fdp); THREAD_UNLOCK(&event_thr->event.tb_mutex); if (event_thr->event.blocked && event_thr->event.evdog && event_dog_client(event_thr->event.evdog) != stream) event_dog_notify(event_thr->event.evdog); }
int acl_timed_connect(ACL_SOCKET sock, const struct sockaddr * sa, socklen_t len, int timeout) { int err; /* * Sanity check. Just like with timed_wait(), the timeout must be a * positive number. */ if (timeout < 0) acl_msg_panic("timed_connect: bad timeout: %d", timeout); /* * Start the connection, and handle all possible results. */ if (acl_sane_connect(sock, sa, len) == 0) return (0); errno = acl_last_error(); #ifdef ACL_UNIX if (errno != ACL_EINPROGRESS) return (-1); #elif defined(ACL_WINDOWS) if (errno != ACL_EINPROGRESS && errno != ACL_EWOULDBLOCK) return (-1); #endif /* * A connection is in progress. Wait for a limited amount of time for * something to happen. If nothing happens, report an error. */ if (acl_write_wait(sock, timeout) < 0) return (-1); /* * Something happened. Some Solaris 2 versions have getsockopt() itself * return the error, instead of returning it via the parameter list. */ len = sizeof(err); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len) < 0) { #ifdef SUNOS5 /* * Solaris 2.4's socket emulation doesn't allow you * to determine the error from a failed non-blocking * connect and just returns EPIPE. Create a fake * error message for connect. -- [email protected] */ if (errno == EPIPE) acl_set_error(ACL_ENOTCONN); #endif return (-1); } if (err != 0) { acl_set_error(err); return (-1); } else return (0); }
int acl_master_flow_put(int len) { char myname[] = "acl_master_flow_put"; char buf[BUFFER_SIZE]; int count; int n = 0; /* * Sanity check. */ if (len <= 0) acl_msg_panic("%s: bad length %d", myname, len); /* * Write or discard N bytes. */ memset(buf, 0, len > BUFFER_SIZE ? BUFFER_SIZE : len); for (count = len; count > 0; count -= n) { n = write(ACL_MASTER_FLOW_WRITE, buf, count > BUFFER_SIZE ? BUFFER_SIZE : count); if (n < 0) return (-1); } if (acl_msg_verbose) acl_msg_info("%s: %d %d", myname, len, len - count); return (len - count); }
const char *tls_dns_name(const GENERAL_NAME *gn, const TLS_SESS_STATE *TLScontext) { const char *myname = "tls_dns_name"; char *cp; const char *dnsname; int len; /* * Peername checks are security sensitive, carefully scrutinize the * input! */ if (gn->type != GEN_DNS) acl_msg_panic("%s: Non DNS input argument", myname); /* * We expect the OpenSSL library to construct GEN_DNS extesion objects as * ASN1_IA5STRING values. Check we got the right union member. */ if (ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) { acl_msg_warn("%s: %s: invalid ASN1 value type in subjectAltName", myname, TLScontext->namaddr); return (0); } /* * Safe to treat as an ASCII string possibly holding a DNS name */ dnsname = (char *) ASN1_STRING_data(gn->d.ia5); len = ASN1_STRING_length(gn->d.ia5); TRIM0(dnsname, len); /* * Per Dr. Steven Henson of the OpenSSL development team, ASN1_IA5STRING * values can have internal ASCII NUL values in this context because * their length is taken from the decoded ASN1 buffer, a trailing NUL is * always appended to make sure that the string is terminated, but the * ASN.1 length may differ from strlen(). */ if (len != (int) strlen(dnsname)) { acl_msg_warn("%s: %s: internal NUL in subjectAltName", myname, TLScontext->namaddr); return 0; } /* * XXX: Should we be more strict and call valid_hostname()? So long as * the name is safe to handle, if it is not a valid hostname, it will not * compare equal to the expected peername, so being more strict than * "printable" is likely excessive... */ if (*dnsname && !allprint(dnsname)) { cp = acl_mystrdup(dnsname); acl_msg_warn("%s: %s: non-printable characters in subjectAltName: %.100s", myname, TLScontext->namaddr, printable(cp, '?')); acl_myfree(cp); return 0; } return (dnsname); }
/* use LockFileEx/UnlockFileEx */ int acl_myflock(ACL_FILE_HANDLE fd, int lock_style acl_unused, int operation) { const char *myname = "acl_myflock"; DWORD size = 0xFFFFFFFF; char ebuf[256]; unsigned char lock_op; OVERLAPPED ovlp; DWORD flags; if ((operation & (ACL_FLOCK_OP_BITS)) != operation) acl_msg_panic("myflock: improper operation type: 0x%x", operation); memset(&ovlp, 0, sizeof(ovlp)); ovlp.Offset = 0; lock_op = operation & ~ACL_FLOCK_OP_NOWAIT; if (lock_op == ACL_FLOCK_OP_NONE) { if(UnlockFileEx(fd, 0, 1, 0, &ovlp)) return (0); acl_msg_error("%s(%d): unlock error(%s)", myname, __LINE__, acl_last_strerror(ebuf,sizeof(ebuf))); return (-1); } else if (lock_op == ACL_FLOCK_OP_SHARED) { while (1) { flags = 0; if ((operation & ACL_FLOCK_OP_NOWAIT)) flags |= LOCKFILE_FAIL_IMMEDIATELY; if(LockFileEx(fd, flags, 0, 1, 0, &ovlp)) return (0); if ((operation & ACL_FLOCK_OP_NOWAIT)) { acl_msg_error("%s(%d): lock error(%s)", myname, __LINE__, acl_last_strerror(ebuf,sizeof(ebuf))); return (-1); } sleep(1); } } else if (lock_op == ACL_FLOCK_OP_EXCLUSIVE) { while (1) { flags = LOCKFILE_EXCLUSIVE_LOCK; if ((operation & ACL_FLOCK_OP_NOWAIT)) flags |= LOCKFILE_FAIL_IMMEDIATELY; if(LockFileEx(fd, flags, 0, 1, 0, &ovlp)) return (0); if ((operation & ACL_FLOCK_OP_NOWAIT)) { acl_msg_error("%s(%d): lock error(%s)", myname, __LINE__, acl_last_strerror(ebuf,sizeof(ebuf))); return (-1); } sleep(1); } } acl_msg_error("%s(%d): invalid lock_op(%d)", myname, __LINE__, lock_op); return (-1); }
void acl_watchdog_stop(ACL_WATCHDOG *wp) { const char* myname = "acl_watchdog_stop"; if (wp != acl_watchdog_curr) acl_msg_panic("%s: wrong watchdog instance", myname); alarm(0); if (acl_msg_verbose > 1) acl_msg_info("%s: %p", myname, (void *) wp); }
void acl_watchdog_start(ACL_WATCHDOG *wp) { const char* myname = "acl_watchdog_start"; if (wp != acl_watchdog_curr) acl_msg_panic("%s: wrong watchdog instance", myname); wp->trip_run = 0; alarm(wp->timeout); if (acl_msg_verbose > 1) acl_msg_info("%s: %p", myname, (void *) wp); }
acl_int64 acl_event_request_timer(ACL_EVENT *eventp, ACL_EVENT_NOTIFY_TIME callback, void *context, acl_int64 delay, int keep) { const char *myname = "acl_event_request_timer"; if (delay < 0) acl_msg_panic("%s: invalid delay: %lld", myname, delay); return eventp->timer_request(eventp, callback, context, delay, keep); }
static ssize_t tls_timed_write(int fd, void *buf, size_t len, int timeout, void *context) { const char *myname = "tls_timed_write"; TLS_SESS_STATE *TLScontext; TLScontext = (TLS_SESS_STATE *) context; if (!TLScontext) acl_msg_panic("%s: no context, buf(%s), len(%d)", myname, (char*) buf, (int) len); if (TLScontext->log_level >= 4) acl_msg_info("Write %ld chars: %.*s", (long) len, (int) (len > 40 ? 40 : len), (char *) buf); return (tls_bio_write(fd, buf, (int) len, timeout, TLScontext)); }
static ssize_t tls_timed_read(int fd, void *buf, size_t len, int timeout, void *context) { const char *myname = "tls_timed_read"; ssize_t ret; TLS_SESS_STATE *TLScontext; TLScontext = (TLS_SESS_STATE *) context; if (!TLScontext) acl_msg_panic("%s: no context", myname); ret = tls_bio_read(fd, buf, (int) len, timeout, TLScontext); if (ret > 0 && TLScontext->log_level >= 4) acl_msg_info("Read %ld chars: %.*s", (long) ret, (int) (ret > 40 ? 40 : ret), (char *) buf); return (ret); }
int acl_timed_waitpid(pid_t pid, ACL_WAIT_STATUS_T *statusp, int options, int time_limit) { const char *myname = "timed_waitpid"; struct sigaction action; struct sigaction old_action; int time_left; int wpid; char tbuf[256]; /* * Sanity checks. */ if (time_limit <= 0) acl_msg_panic("%s: bad time limit: %d", myname, time_limit); /* * Set up a timer. */ sigemptyset(&action.sa_mask); action.sa_flags = 0; action.sa_handler = timed_wait_alarm; if (sigaction(SIGALRM, &action, &old_action) < 0) acl_msg_fatal("%s: sigaction(SIGALRM): %s", myname, acl_last_strerror(tbuf, sizeof(tbuf))); timed_wait_expired = 0; time_left = alarm(time_limit); /* * Wait for only a limited amount of time. */ if ((wpid = waitpid(pid, statusp, options)) < 0 && timed_wait_expired) acl_set_error(ETIMEDOUT); /* * Cleanup. */ alarm(0); if (sigaction(SIGALRM, &old_action, (struct sigaction *) 0) < 0) acl_msg_fatal("%s: sigaction(SIGALRM): %s", myname, acl_last_strerror(tbuf, sizeof(tbuf))); if (time_left) alarm(time_left); return (wpid); }
ACL_VSTRING *tok822_internalize(ACL_VSTRING *vp, TOK822 *tree, int flags) { TOK822 *tp; if (flags & TOK822_STR_WIPE) ACL_VSTRING_RESET(vp); for (tp = tree; tp; tp = tp->next) { switch (tp->type) { case ',': ACL_VSTRING_ADDCH(vp, tp->type); if (flags & TOK822_STR_LINE) { ACL_VSTRING_ADDCH(vp, '\n'); continue; } break; case TOK822_ADDR: tok822_internalize(vp, tp->head, TOK822_STR_NONE); break; case TOK822_COMMENT: case TOK822_ATOM: case TOK822_QSTRING: acl_vstring_strcat(vp, acl_vstring_str(tp->vstr)); break; case TOK822_DOMLIT: ACL_VSTRING_ADDCH(vp, '['); acl_vstring_strcat(vp, acl_vstring_str(tp->vstr)); ACL_VSTRING_ADDCH(vp, ']'); break; case TOK822_STARTGRP: ACL_VSTRING_ADDCH(vp, ':'); break; default: if (tp->type >= TOK822_MINTOK) acl_msg_panic("tok822_internalize: unknown operator %d", tp->type); ACL_VSTRING_ADDCH(vp, tp->type); } if (tok822_append_space(tp)) ACL_VSTRING_ADDCH(vp, ' '); } if (flags & TOK822_STR_TERM) ACL_VSTRING_TERMINATE(vp); return (vp); }
static SSL_SESSION *load_clnt_session(TLS_SESS_STATE *TLScontext) { const char *myname = "load_clnt_session"; SSL_SESSION *session = 0; ACL_VSTRING *session_data = acl_vstring_alloc(2048); /* * Prepare the query. */ if (TLScontext->log_level >= 2) acl_msg_info("looking for session %s in %s cache", TLScontext->serverid, TLScontext->cache_type); /* * We only get here if the cache_type is not empty. This code is not * called unless caching is enabled and the cache_type is stored in the * server SSL context. */ if (TLScontext->cache_type == 0) acl_msg_panic("%s: null client session cache type in session lookup", myname); /* * Look up and activate the SSL_SESSION object. Errors are non-fatal, * since caching is only an optimization. */ if (tls_mgr_lookup(TLScontext->cache_type, TLScontext->serverid, session_data) == TLS_MGR_STAT_OK) { session = tls_session_activate(STR(session_data), (int) LEN(session_data)); if (session) { if (TLScontext->log_level >= 2) acl_msg_info("reloaded session %s from %s cache", TLScontext->serverid, TLScontext->cache_type); } } /* * Clean up. */ acl_vstring_free(session_data); return (session); }
/* use LockFile/UnlockFile */ int acl_myflock(ACL_FILE_HANDLE fd, int lock_style acl_unused, int operation) { const char *myname = "acl_myflock"; DWORD size = 0xFFFFFFFF; char ebuf[256]; unsigned char lock_op; if ((operation & (ACL_FLOCK_OP_BITS)) != operation) acl_msg_panic("myflock: improper operation type: 0x%x", operation); lock_op = operation & ~ACL_FLOCK_OP_NOWAIT; if (lock_op == ACL_FLOCK_OP_NONE) { if(UnlockFile(fd, 0, 0, size, 0)) return (0); acl_msg_error("%s(%d): unlock error(%s)", myname, __LINE__, acl_last_strerror(ebuf,sizeof(ebuf))); return (-1); } else if (lock_op == ACL_FLOCK_OP_SHARED) { while (1) { if(LockFile(fd, 0, 0, size, 0)) return (0); if ((operation & ACL_FLOCK_OP_NOWAIT)) return (-1); sleep(1); } } else if (lock_op == ACL_FLOCK_OP_EXCLUSIVE) { while (1) { if(LockFile(fd, 0, 0, size, 0)) return (0); if ((operation & ACL_FLOCK_OP_NOWAIT)) return (-1); sleep(1); } } acl_msg_error("%s(%d): invalid lock_op(%d)", myname, __LINE__, lock_op); return (-1); }
ssize_t tls_prng_egd_read(TLS_PRNG_SRC *egd, size_t len) { const char *myname = "tls_prng_egd_read"; unsigned char buffer[UCHAR_MAX]; ssize_t count; if (len <= 0) acl_msg_panic("%s: bad length %ld", myname, (long) len); buffer[0] = 1; buffer[1] = (unsigned char) (len > UCHAR_MAX ? UCHAR_MAX : len); if (acl_timed_write(egd->fd.sock, buffer, 2, egd->timeout, (void *) 0) != 2) { acl_msg_info("%s: cannot write to EGD server %s: %s", myname, egd->name, acl_last_serror()); return (-1); } if (acl_timed_read(egd->fd.sock, buffer, 1, egd->timeout, (void *) 0) != 1) { acl_msg_info("%s: cannot read from EGD server %s: %s", myname, egd->name, acl_last_serror()); return (-1); } count = buffer[0]; if (count > (ssize_t) sizeof(buffer)) count = sizeof(buffer); if (count == 0) { acl_msg_info("EGD server %s reports zero bytes available", egd->name); return (-1); } if (acl_timed_read(egd->fd.sock, buffer, count, egd->timeout, (void *) 0) != count) { acl_msg_info("%s: cannot read %ld bytes from EGD server %s: %s", myname, (long) count, egd->name, acl_last_serror()); return (-1); } if (acl_msg_verbose) acl_msg_info("%s: got %ld bytes from EGD server %s", myname, (long) count, egd->name); RAND_seed(buffer, count); return (count); }
static void acl_watchdog_event(int unused_sig acl_unused) { const char* myname = "acl_watchdog_event"; ACL_WATCHDOG *wp; /* * This routine runs as a signal handler. We should not do anything * that could involve memory allocation/deallocation, but exiting * without proper explanation would be unacceptable. */ if ((wp = acl_watchdog_curr) == 0) acl_msg_panic("%s: no instance", myname); if (acl_msg_verbose > 1) acl_msg_info("%s: %p %d", myname, (void *) wp, wp->trip_run); if (++(wp->trip_run) < ACL_WATCHDOG_STEPS) alarm(wp->timeout); else { if (wp->action) wp->action(wp, wp->context); else acl_msg_fatal("watchdog timeout"); } }
int acl_master_flow_get(int len) { char myname[] = "acl_master_flow_get"; char buf[BUFFER_SIZE]; struct stat st; int count; int n = 0; /* * Sanity check. */ if (len <= 0) acl_msg_panic("%s: bad length %d", myname, len); /* * Silence some wild claims. */ if (fstat(ACL_MASTER_FLOW_WRITE, &st) < 0) acl_msg_fatal("fstat flow pipe write descriptor: %s", strerror(errno)); /* * Read and discard N bytes. XXX AIX read() can return 0 when an open * pipe is empty. */ for (count = len; count > 0; count -= n) { n = read(ACL_MASTER_FLOW_READ, buf, count > BUFFER_SIZE ? BUFFER_SIZE : count); if (n <= 0) return (-1); } if (acl_msg_verbose) acl_msg_info("%s: %d %d", myname, len, len - count); return (len - count); }
void http_chat_sync_resctl(HTTP_RES *respond, int name, ...) { const char *myname = "http_chat_sync_resctl"; va_list ap; int n; va_start(ap, name); for (; name != HTTP_CHAT_SYNC_CTL_END; name = va_arg(ap, int)) { switch (name) { case HTTP_CHAT_CTL_BUFF_ONOFF: n = va_arg(ap, int); if (n) respond->flag |= HTTP_CHAT_FLAG_BUFFED; else respond->flag &= ~HTTP_CHAT_FLAG_BUFFED; break; default: acl_msg_panic("%s, %s(%d): bad name %d", myname, __FILE__, __LINE__, name); break; } } va_end(ap); }
int tls_bio(ACL_SOCKET fd, int timeout, TLS_SESS_STATE *TLScontext, int (*hsfunc) (SSL *), int (*rfunc) (SSL *, void *, int), int (*wfunc) (SSL *, const void *, int), void *buf, int num) { const char *myname = "tls_bio"; int status = 0; int err; int retval = 0; int biop_retval; int done; /* * If necessary, retry the SSL handshake or read/write operation after * handling any pending network I/O. */ for (done = 0; done == 0; /* void */ ) { if (hsfunc) { #if 1 status = hsfunc(TLScontext->con); #else status = SSL_do_handshake(TLScontext->con); #endif } else if (rfunc) status = rfunc(TLScontext->con, buf, num); else if (wfunc) status = wfunc(TLScontext->con, buf, num); else acl_msg_panic("%s: nothing to do here", myname); err = SSL_get_error(TLScontext->con, status); #if (OPENSSL_VERSION_NUMBER <= 0x0090581fL) /* * There is a bug up to and including OpenSSL-0.9.5a: if an error * occurs while checking the peers certificate due to some * certificate error (e.g. as happend with a RSA-padding error), the * error is put onto the error stack. If verification is not * enforced, this error should be ignored, but the error-queue is not * cleared, so we can find this error here. The bug has been fixed on * May 28, 2000. * * This bug so far has only manifested as 4800:error:0407006A:rsa * routines:RSA_padding_check_PKCS1_type_1:block type is not * 01:rsa_pk1.c:100: 4800:error:04067072:rsa * routines:RSA_EAY_PUBLIC_DECRYPT:padding check * failed:rsa_eay.c:396: 4800:error:0D079006:asn1 encoding * routines:ASN1_verify:bad get asn1 object call:a_verify.c:109: so * that we specifically test for this error. We print the errors to * the logfile and automatically clear the error queue. Then we retry * to get another error code. We cannot do better, since we can only * retrieve the last entry of the error-queue without actually * cleaning it on the way. * * This workaround is secure, as verify_result is set to "failed" * anyway. */ if (err == SSL_ERROR_SSL) { if (ERR_peek_error() == 0x0407006AL) { tls_print_errors(); acl_msg_info("OpenSSL <= 0.9.5a workaround called: certificate errors ignored"); err = SSL_get_error(TLScontext->con, status); } } #endif /* * Find out if we must retry the operation and/or if there is pending * network I/O. * * XXX If we're the first to invoke SSL_shutdown(), then the operation * isn't really complete when the call returns. We could hide that * anomaly here and repeat the call. */ switch (err) { case SSL_ERROR_NONE: /* success */ retval = status; done = 1; /* FALLTHROUGH */ case SSL_ERROR_WANT_WRITE: /* flush/update buffers */ case SSL_ERROR_WANT_READ: biop_retval = network_biopair_interop(fd, timeout, TLScontext->network_bio); if (biop_retval < 0) return (-1); /* network read/write error */ break; /* * With tls_timed_read() and tls_timed_write() the caller is the * VSTREAM library module which is unaware of TLS, so we log the * TLS error stack here. In a better world, each VSTREAM I/O * object would provide an error reporting method in addition to * the timed_read and timed_write methods, so that we would not * need to have ad-hoc code like this. */ case SSL_ERROR_SSL: if (rfunc || wfunc) tls_print_errors(); /* FALLTHROUGH */ default: retval = status; done = 1; break; } } return (retval); }
static int network_biopair_interop(ACL_SOCKET fd, int timeout, BIO *network_bio) { const char *myname = "network_biopair_interop"; int want_write; int num_write; int write_pos; int from_bio; int want_read; int num_read; int to_bio; char buffer[NETLAYER_BUFFERSIZE]; /* * To avoid deadlock, write all pending data to the network before * attempting to read from the network. */ while ((want_write = (int) BIO_ctrl_pending(network_bio)) > 0) { if (want_write > (int) sizeof(buffer)) want_write = (int) sizeof(buffer); from_bio = BIO_read(network_bio, buffer, want_write); /* * Write the complete buffer contents to the network. */ for (write_pos = 0; write_pos < from_bio; /* see below */ ) { if (timeout > 0 && acl_write_wait(fd, timeout) < 0) return (-1); num_write = acl_socket_write(fd, buffer + write_pos, from_bio - write_pos, 0, 0, 0); if (num_write <= 0) { if ((num_write < 0) && (timeout > 0) && (errno == ACL_EAGAIN || errno == ACL_EINTR)) { acl_msg_warn("%s: write() returns EAGAIN on a writable file descriptor!", myname); acl_msg_warn("%s: pausing to avoid going into a tight select/write loop!", myname); sleep(1); } else { acl_msg_warn("%s: error writing %d bytes to the network: %s", myname, from_bio - write_pos, acl_last_serror()); return (-1); } } else { write_pos += num_write; } } } /* * Read data from the network into the BIO pair. */ while ((want_read = (int) BIO_ctrl_get_read_request(network_bio)) > 0) { if (want_read > (int) sizeof(buffer)) want_read = (int) sizeof(buffer); if (timeout > 0 && acl_read_wait(fd, timeout) < 0) return (-1); num_read = acl_socket_read(fd, buffer, want_read, 0, 0, 0); if (num_read == 0) /* FIX 200412 Cannot return a zero read count. */ return (-1); if (num_read < 0) { if ((num_read < 0) && (timeout > 0) && (errno == ACL_EAGAIN || errno == ACL_EINTR)) { acl_msg_warn("%s: read() returns EAGAIN on a readable file descriptor!", myname); acl_msg_warn("%s: pausing to avoid going into a tight select/write loop!", myname); sleep(1); } else { acl_msg_warn("%s: error reading %d bytes from the network: %s", myname, want_read, acl_last_serror()); return (-1); } } else { to_bio = BIO_write(network_bio, buffer, num_read); if (to_bio != num_read) acl_msg_panic("%s: BIO_write error: to_bio != num_read", myname); } } return (0); }
ACL_VSTRING *tok822_externalize(ACL_VSTRING *vp, TOK822 *tree, int flags) { ACL_VSTRING *tmp; TOK822 *tp; ssize_t start = 0; TOK822 *addr = 0; ssize_t addr_len = 0; /* * Guard against a Sendmail buffer overflow (CERT advisory CA-2003-07). * The problem was that Sendmail could store too much non-address text * (comments, phrases, etc.) into a static 256-byte buffer. * * When the buffer fills up, fixed Sendmail versions remove comments etc. * and reduce the information to just <$g>, which expands to <address>. * No change is made when an address expression (text separated by * commas) contains no address. This fix reportedly also protects * Sendmail systems that are still vulnerable to this problem. * * Postfix takes the same approach, grudgingly. To avoid unnecessary damage, * Postfix removes comments etc. only when the amount of non-address text * in an address expression (text separated by commas) exceeds 250 bytes. * * With Sendmail, the address part of an address expression is the * right-most <> instance in that expression. If an address expression * contains no <>, then Postfix guarantees that it contains at most one * non-comment string; that string is the address part of the address * expression, so there is no ambiguity. * * Finally, we note that stress testing shows that other code in Sendmail * 8.12.8 bluntly truncates ``text <address>'' to 256 bytes even when * this means chopping the <address> somewhere in the middle. This is a * loss of control that we're not entirely comfortable with. However, * unbalanced quotes and dangling backslash do not seem to influence the * way that Sendmail parses headers, so this is not an urgent problem. */ #define MAX_NONADDR_LENGTH 250 #define RESET_NONADDR_LENGTH { \ start = ACL_VSTRING_LEN(vp); \ addr = 0; \ addr_len = 0; \ } #define ENFORCE_NONADDR_LENGTH do { \ if (addr && (ssize_t) ACL_VSTRING_LEN(vp) - addr_len > start + MAX_NONADDR_LENGTH) \ strip_address(vp, start, addr->head); \ } while(0) if (flags & TOK822_STR_WIPE) ACL_VSTRING_RESET(vp); if (flags & TOK822_STR_TRNC) RESET_NONADDR_LENGTH; for (tp = tree; tp; tp = tp->next) { switch (tp->type) { case ',': if (flags & TOK822_STR_TRNC) ENFORCE_NONADDR_LENGTH; ACL_VSTRING_ADDCH(vp, tp->type); ACL_VSTRING_ADDCH(vp, (flags & TOK822_STR_LINE) ? '\n' : ' '); if (flags & TOK822_STR_TRNC) RESET_NONADDR_LENGTH; continue; /* * XXX In order to correctly externalize an address, it is not * sufficient to quote individual atoms. There are higher-level * rules that say when an address localpart needs to be quoted. * We wing it with the quote_822_local() routine, which ignores * the issue of atoms in the domain part that would need quoting. */ case TOK822_ADDR: addr = tp; tmp = acl_vstring_alloc(100); tok822_internalize(tmp, tp->head, TOK822_STR_TERM); addr_len = ACL_VSTRING_LEN(vp); quote_822_local_flags(vp, acl_vstring_str(tmp), QUOTE_FLAG_8BITCLEAN | QUOTE_FLAG_APPEND); addr_len = ACL_VSTRING_LEN(vp) - addr_len; acl_vstring_free(tmp); break; case TOK822_ATOM: case TOK822_COMMENT: acl_vstring_strcat(vp, acl_vstring_str(tp->vstr)); break; case TOK822_QSTRING: ACL_VSTRING_ADDCH(vp, '"'); tok822_copy_quoted(vp, acl_vstring_str(tp->vstr), "\"\\\r\n"); ACL_VSTRING_ADDCH(vp, '"'); break; case TOK822_DOMLIT: ACL_VSTRING_ADDCH(vp, '['); tok822_copy_quoted(vp, acl_vstring_str(tp->vstr), "\\\r\n"); ACL_VSTRING_ADDCH(vp, ']'); break; case TOK822_STARTGRP: ACL_VSTRING_ADDCH(vp, ':'); break; case '<': if (tp->next && tp->next->type == '>') { addr = tp; addr_len = 0; } ACL_VSTRING_ADDCH(vp, '<'); break; default: if (tp->type >= TOK822_MINTOK) acl_msg_panic("tok822_externalize: unknown operator %d", tp->type); ACL_VSTRING_ADDCH(vp, tp->type); } if (tok822_append_space(tp)) ACL_VSTRING_ADDCH(vp, ' '); } if (flags & TOK822_STR_TRNC) ENFORCE_NONADDR_LENGTH; if (flags & TOK822_STR_TERM) ACL_VSTRING_TERMINATE(vp); return (vp); }
static void event_enable_read(ACL_EVENT *eventp, ACL_VSTREAM *stream, int timeout, ACL_EVENT_NOTIFY_RDWR callback, void *context) { const char *myname = "event_enable_read"; EVENT_SELECT_THR *event_thr = (EVENT_SELECT_THR *) eventp; ACL_EVENT_FDTABLE *fdp; ACL_SOCKET sockfd; sockfd = ACL_VSTREAM_SOCK(stream); THREAD_LOCK(&event_thr->event.tb_mutex); /* * Disallow multiple requests on the same file descriptor. * Allow duplicates of the same request. */ if (FD_ISSET(sockfd, &event_thr->wmask)) acl_msg_panic("%s(%d), %s: fd %d: multiple I/O request", __FILE__, __LINE__, myname, sockfd); fdp = stream->fdp; if (fdp == NULL) fdp = event_fdtable_alloc(); if (fdp->flag & EVENT_FDTABLE_FLAG_WRITE) acl_msg_panic("%s(%d)->%s: fd %d: multiple I/O request", __FILE__, __LINE__, myname, sockfd); if ((fdp->flag & EVENT_FDTABLE_FLAG_READ) == 0) { fdp->flag = EVENT_FDTABLE_FLAG_READ | EVENT_FDTABLE_FLAG_EXPT; if (FD_ISSET(sockfd, &event_thr->rmask)) acl_msg_fatal("%s, %s(%d): sockfd(%d) has been in rmask", myname, __FILE__, __LINE__, sockfd); FD_SET(sockfd, &event_thr->xmask); FD_SET(sockfd, &event_thr->rmask); stream->fdp = (void *) fdp; stream->nrefer++; fdp->stream = stream; fdp->listener = 0; fdp->fdidx = eventp->fdcnt; eventp->fdtabs[eventp->fdcnt] = fdp; eventp->fdcnt++; if (eventp->maxfd != ACL_SOCKET_INVALID && eventp->maxfd < sockfd) eventp->maxfd = sockfd; } if (fdp->r_callback != callback || fdp->r_context != context) { fdp->r_callback = callback; fdp->r_context = context; } if (timeout > 0) { fdp->r_timeout = timeout * 1000000; fdp->r_ttl = eventp->event_present + fdp->r_timeout; } else { fdp->r_ttl = 0; fdp->r_timeout = 0; } THREAD_UNLOCK(&event_thr->event.tb_mutex); /* 主要是为了减少通知次数 */ if (event_thr->event.blocked && event_thr->event.evdog && event_dog_client(event_thr->event.evdog) != stream) event_dog_notify(event_thr->event.evdog); }