/** * Inialialize key_file and cert_file variables */ int init_x509_filenames() { #if USE_X509 if (!key_file) { key_file = (char *) calloc(strlen(CONFIG_DIR) + strlen(KEYFILE) + 2, sizeof(char)); if (!key_file) { log_area_printf (DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: cannot allocate the key file"); return 0; } strcat(key_file, CONFIG_DIR); strcat(key_file, "/"); strcat(key_file, KEYFILE); } if (!cert_file) { cert_file = (char *) calloc(strlen(CONFIG_DIR) + strlen(CERTFILE) + 2, sizeof(char)); if (!cert_file) { log_area_printf (DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: cannot allocate the cert file"); return 0; } strcat(cert_file, CONFIG_DIR); strcat(cert_file, "/"); strcat(cert_file, CERTFILE); } #endif return 1; }
/** * Process authentication server (NuAuth) packet answer. Different answers * can be: * - Decision answer: packet accepted/rejected * - Connection destroy: ask conntrack to destroy a connection * - Connection update: ask conntrack to set connection timeout to given * value * * \return -1 in case of error */ static int auth_packet_to_decision(char *dgram, int dgram_size) { if (dgram_size < 2) { debug_log_printf(DEBUG_AREA_GW, DEBUG_LEVEL_DEBUG, "NuAuth sent too small message"); return -1; } if (dgram[0] != PROTO_NUFW_VERSION) { debug_log_printf(DEBUG_AREA_GW, DEBUG_LEVEL_DEBUG, "Wrong protocol version from authentication server answer."); return -1; } switch (dgram[1]) { case AUTH_ANSWER: return auth_process_answer(dgram, dgram_size); case AUTH_CONN_DESTROY: log_area_printf(DEBUG_AREA_MAIN | DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "Connection destroy message not supported"); break; case AUTH_CONN_UPDATE: log_area_printf(DEBUG_AREA_MAIN | DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "Connection update message not supported"); break; default: log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_DEBUG, "NuAuth message type %d not for me", dgram[1]); break; } return -1; }
static void packetsrv_activity_cb(struct ev_loop *loop, ev_io *w, int revents) { if (revents & EV_READ) { unsigned char buffer[BUFSIZ]; int rv; int fd = nfq_fd(h); /* read one packet */ rv = recv(fd, buffer, sizeof(buffer), 0); if (rv < 0) { struct nlif_handle *nlif_handle = (struct nlif_handle *) w->data; log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "[!] Error of read on netfilter queue socket (code %i, errno %d): %s!", rv, errno, strerror(errno)); log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_SERIOUS_MESSAGE, "Reopen netlink connection."); packetsrv_close(0); #ifdef HAVE_NFQ_INDEV_NAME fd = packetsrv_open(nlif_handle); #else fd = packetsrv_open(NULL); #endif if (fd < 0) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "[!] FATAL ERROR: Fail to reopen netlink connection!"); exit(EXIT_FAILURE); } ev_io_stop(loop, w); ev_io_set(w, fd, EV_READ); ev_io_start(loop, w); return; } ev_io_stop(loop, &tls.ev_io); /* process the packet */ nfq_handle_packet(h, (char *) buffer, rv); pckt_rx++; ev_io_start(loop, &tls.ev_io); } if (revents & EV_ERROR) { struct nlif_handle *nlif_handle = (struct nlif_handle *) w->data; int fd; packetsrv_close(0); ev_io_stop(loop, w); #ifdef HAVE_NFQ_INDEV_NAME fd = packetsrv_open(nlif_handle); #else fd = packetsrv_open(NULL); #endif ev_io_set(w, fd, EV_READ); ev_io_start(loop, w); } }
void tls_connect_unix() { struct sockaddr_un remote; socklen_t len; int s; int ret; ufwissl_session* sess; int opt; log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_FATAL, "Trying to connect to unix socket: %s", authreq_addr); s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_DEBUG, "Couldn't create socket"); return; } remote.sun_family = AF_UNIX; strncpy(remote.sun_path, authreq_addr, UNIX_MAX_PATH-1); len = strlen(remote.sun_path) + sizeof(remote.sun_family); ret = connect(s, (struct sockaddr *)&remote, len); if (ret < 0) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_DEBUG, "Couldn't connect to unix socket"); return; } if (buffer_size != 0) { opt = buffer_size; ret = setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, &opt, sizeof(opt)); if (ret < 0) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_DEBUG, "Couldn't set send buffer size to unix socket: %s", strerror(errno)); return; } } sess = ufwissl_session_create_with_fd(s, 0 /* verify */); /* time *must* be set to 0 (non-blocking calls) to avoid a deadlock * between auth_request_send and authsrv */ ufwissl_set_read_timeout(sess, 0); tls.session = sess; }
static void tls_activity_cb(struct ev_loop *loop, ev_io *w, int revents) { ev_io *nfq_watcher = (ev_io *) w->data; int ret; int i = 0; if (revents & EV_READ) { ev_io_stop(loop, w); ev_io_stop(loop, nfq_watcher); do { if (i>0) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_VERBOSE_DEBUG, "remaining data to read"); } /* TODO improve function type here */ ret = authsrv(NULL); } while ((ret != NU_EXIT_ERROR) && (nussl_read_available(tls.session))); ev_io_start(loop, nfq_watcher); if (tls.session) { ev_io_start(loop, w); } i++; } if (revents & EV_ERROR) { tls.auth_server_running = 0; } }
static void cleaning_timer_cb(struct ev_loop *loop, ev_timer *w, int revents) { int stat_rx, stat_tx; ev_io *nfq_watcher = (ev_io *) w->data; ev_io_stop(loop, &tls.ev_io); ev_io_stop(loop, nfq_watcher); clean_old_packets(); ev_io_start(loop, nfq_watcher); ev_io_start(loop, &tls.ev_io); #ifdef DEBUG_ENABLE /* display stats */ /* FIXME : modify this function */ /* process_poll(0); */ stat_rx = pckt_rx - p_pckt_rx; p_pckt_rx = pckt_rx; stat_tx = pckt_rx - p_pckt_rx; p_pckt_rx = pckt_rx; log_area_printf(DEBUG_AREA_MAIN | DEBUG_AREA_PACKET, DEBUG_LEVEL_DEBUG, "Average: rx=%.2f, tx=%.2f", (1.0 * stat_rx) / CLEANING_DELAY, (1.0 * stat_tx) / CLEANING_DELAY); #endif }
void tls_crl_update_user_session(GSList *session) { GSList *listrunner = session; int ret; while ( listrunner ) { struct nuauth_thread_t *nuauth_thread = listrunner->data; struct tls_user_context_t *context = nuauth_thread->data; // Don't update the CRL when nufw is not yet connected if (context->nussl == NULL) { listrunner = g_slist_next(listrunner); continue; } g_mutex_lock(nuauth_thread->mutex); ret = nussl_ssl_set_crl_file(context->nussl, nuauth_tls.crl_file, nuauth_tls.ca); if (ret != NUSSL_OK) { log_area_printf(DEBUG_AREA_USER, DEBUG_LEVEL_CRITICAL, "User TLS: CRL file reloading failed (%s)", nussl_get_error(context->nussl)); } g_mutex_unlock(nuauth_thread->mutex); listrunner = g_slist_next(listrunner); } g_slist_free(listrunner); }
void packetsrv_close(int smart) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_SERIOUS_MESSAGE, "Destroy netfilter queue socket"); if (smart) nfq_destroy_queue(hndl); nfq_close(h); /* packet id will be reset after restart, we discard waiting packets */ clear_packet_list(); }
/** * Halt TLS threads and close socket */ void shutdown_tls() { if (!tls.auth_server_running) return; log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_CRITICAL, "tls send failure when sending request"); close_tls_session(); /* put auth_server_running to 0 because this is this thread which has * just killed auth_server */ tls.auth_server_running = 0; }
/** * Parse an packet and check if it's TCP in IPv4 packet with TCP flag * ACK, FIN or RST set. * * \param dgram Pointer to data to parse * \param datalen Length of the data * \return If the TCP if the packet matchs, returns 1. Else, returns 0. */ int look_for_tcp_flags(unsigned char *dgram, unsigned int datalen) { struct iphdr *iphdrs = (struct iphdr *) dgram; /* check need some data */ if (datalen < sizeof(struct iphdr) + sizeof(struct tcphdr)) { log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_VERBOSE_DEBUG, "Incorrect packet data length"); return 0; } /* check IP version */ if (iphdrs->version == 4) { if (iphdrs->protocol == IPPROTO_TCP) { struct tcphdr *tcphdrs = (struct tcphdr *) (dgram + 4 * iphdrs->ihl); if (tcphdrs->fin || tcphdrs->ack || tcphdrs->rst) { RETURN_NO_LOG 1; } } } return 0; }
/** * Configure NuAuth: * - Init. glib threads: g_thread_init() ; * - Read command line options: parse_options() ; * - Read NuAuth configuration file: init_nuauthconf() ; * - Build configuration options: build_prenuauthconf() ; * - Init SSL library: nussl_init() ; * - Create credentials: create_x509_credentials() ; * - Daemonize the process if asked: daemonize(). */ void configure_app(int argc, char **argv) { command_line_params_t params; int ret; struct rlimit r_limit; struct nuauth_params initconf; struct nuauth_params *conf; /* Avoid creation of core file which may contains username and password */ if (getrlimit(RLIMIT_CORE, &r_limit) == 0) { #ifdef DEBUG_ENABLE r_limit.rlim_cur = RLIM_INFINITY; #else r_limit.rlim_cur = 0; #endif setrlimit(RLIMIT_CORE, &r_limit); } if (getrlimit(RLIMIT_NOFILE, &r_limit) == 0) { r_limit.rlim_cur = 8096; r_limit.rlim_max = 8096; setrlimit(RLIMIT_NOFILE, &r_limit); } #ifndef DEBUG_ENABLE /* Move to root directory to not block current working directory */ (void) chdir("/"); #endif #ifdef DEBUG_MEMORY g_mem_set_vtable(glib_mem_profiler_table); #endif /* Initialize glib thread system */ g_thread_init(NULL); g_thread_pool_set_max_unused_threads(5); /* init nuauthdatas */ nuauthdatas = g_new0(struct nuauth_datas, 1); nuauthdatas->is_starting = TRUE; nuauthdatas->reload_cond = g_cond_new(); nuauthdatas->reload_cond_mutex = g_mutex_new(); nuauthdatas->program_fullpath = g_strdup(argv[0]); /* set default parameters */ params.debug_level = 0; params.daemonize = 0; params.client_srv = NULL; params.nufw_srv = NULL; params.userpckt_port = NULL; params.authreq_port = NULL; params.configfile = NULL; nuauthconf = &initconf; /* check if configuration file is not given on command line */ parse_options(argc, argv, ¶ms); if (params.debug_level) { nuauthconf->debug_level = params.debug_level; } else { nuauthconf->debug_level = DEFAULT_DEBUG_LEVEL; } nuauthconf->debug_areas = DEFAULT_DEBUG_AREAS; if (params.configfile == NULL) { nuauthconf->configfile = g_strdup(DEFAULT_CONF_FILE); } else { nuauthconf->configfile = g_strdup(params.configfile); } ret = nuauth_parse_configuration(nuauthconf->configfile); if (ret < 0) { log_message(FATAL, DEBUG_AREA_MAIN, "Cannot load configuration (file '%s')", nuauthconf->configfile); exit(EXIT_FAILURE); } /* load configuration */ if (!init_nuauthconf(&conf)) { log_message(FATAL, DEBUG_AREA_MAIN, "Unable to load configuration"); exit(EXIT_FAILURE); } conf->configfile = nuauthconf->configfile; nuauthconf = conf; /* build configuration by overwritting config file option by * command line one */ nuauthconf_from_cmdline(¶ms); build_prenuauthconf(nuauthconf, NULL, 0); ret = nussl_init(); if ( ret != NUSSL_OK ) { log_message(FATAL, DEBUG_AREA_MAIN, "FATAL ERROR: NuSSL global initialisation failed."); exit(EXIT_FAILURE); } if (nuauthconf->uses_utf8) { setlocale(LC_ALL, ""); } /* debug cannot be above 10 */ if (nuauthconf->debug_level > MAX_DEBUG_LEVEL) nuauthconf->debug_level = MAX_DEBUG_LEVEL; if (nuauthconf->debug_level < MIN_DEBUG_LEVEL) nuauthconf->debug_level = MIN_DEBUG_LEVEL; log_message(INFO, DEBUG_AREA_MAIN, "Debug_level is %i", nuauthconf->debug_level); #if 0 /* init credential */ if (! create_x509_credentials()) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_FATAL, "Certificate initialization failed"); exit(EXIT_FAILURE); } #endif /* free command line structure */ g_free(params.client_srv); g_free(params.nufw_srv); g_free(params.userpckt_port); g_free(params.authreq_port); g_free(params.configfile); debug_areas = DEFAULT_DEBUG_AREAS; debug_level = nuauthconf->debug_level; /* init gcrypt and gnutls */ if (params.daemonize == 1) { daemonize(); /* default: use only syslog */ set_glib_loghandlers(0, nuauthconf->log_to_syslog); } else { int use_stdout = (quiet_mode == 0); /* default: use both stdout and syslog */ set_glib_loghandlers(use_stdout, nuauthconf->log_to_syslog); log_message(FATAL, DEBUG_AREA_MAIN, "[+] NuAuth " NUAUTH_FULL_VERSION " with config %s", nuauthconf->configfile); log_message(FATAL, DEBUG_AREA_MAIN, "Nuauth started in foreground (without -D option), " "logging on stderr, stdout and syslog"); } /* declare default capabilities */ register_client_capa("HELLO", &(nuauthdatas->hello_capa)); register_client_capa("TCP", &(nuauthdatas->tcp_capa)); register_client_capa("UDP", &(nuauthdatas->udp_capa)); }
/** * \brief Callback called by NetFilter when a packet with target QUEUE is matched. * * For TCP packet with flags different than SYN, just send it to NuAuth and * accept it. * * For other packet: First of all, fill a structure ::packet_idl (identifier, * timestamp, ...). Try to add the new packet to ::packets_list (fails if the * list is full). Ask an authentication to NuAuth using auth_request_send(), * If the packet can't be sended, remove it from the list. * * \return If an error occurs, returns 0, else returns 1. */ static int treat_packet(struct nfq_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { packet_idl *current; struct queued_pckt q_pckt; struct nfqnl_msg_packet_hdr *ph; struct timeval timestamp; int ret; #ifdef HAVE_NFQ_INDEV_NAME struct nlif_handle *nlif_handle = (struct nlif_handle *) data; #endif debug_log_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_VERBOSE_DEBUG, "(*) New packet"); q_pckt.payload_len = nfq_get_payload(nfa, &(q_pckt.payload)); if (q_pckt.payload_len == -1) { log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_INFO, "Unable to get payload"); return 0; } q_pckt.mark = nfq_get_nfmark(nfa); #ifdef HAVE_NFQ_INDEV_NAME if (!get_interface_information(nlif_handle, &q_pckt, nfa)) { log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_INFO, "Can not get interfaces information for message"); return 0; } #else snprintf(q_pckt.indev, sizeof(q_pckt.indev), "*"); snprintf(q_pckt.physindev, sizeof(q_pckt.physindev), "*"); snprintf(q_pckt.outdev, sizeof(q_pckt.outdev), "*"); snprintf(q_pckt.physoutdev, sizeof(q_pckt.physoutdev), "*"); #endif ret = nfq_get_timestamp(nfa, ×tamp); if (ret == 0) { q_pckt.timestamp = timestamp.tv_sec; } else { q_pckt.timestamp = time(NULL); } if (look_for_tcp_flags ((unsigned char *) q_pckt.payload, q_pckt.payload_len)) { ph = nfq_get_msg_packet_hdr(nfa); if (ph) { q_pckt.packet_id = ntohl(ph->packet_id); auth_request_send(AUTH_CONTROL, &q_pckt); IPQ_SET_VERDICT(q_pckt.packet_id, NF_ACCEPT); RETURN_NO_LOG 1; } else { log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_VERBOSE_DEBUG, "Can not get the packet headers"); return 0; } } current = calloc(1, sizeof(packet_idl)); current->nfmark = q_pckt.mark; current->timestamp = q_pckt.timestamp ; current->id = 0; if (current == NULL) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_MESSAGE, "Can not allocate packet_id"); return 0; } #ifdef PERF_DISPLAY_ENABLE gettimeofday(&(current->arrival_time), NULL); #endif /* Get unique identifier of packet in queue */ ph = nfq_get_msg_packet_hdr(nfa); if (ph) { current->id = ntohl(ph->packet_id); } else { free(current); log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_INFO, "Can not get id for message"); return 0; } /* Try to add the packet to the list */ ret = padd(current); q_pckt.packet_id = current->id; if (ret == 0) { /* send an auth request packet */ if (!auth_request_send(AUTH_REQUEST, &q_pckt)) { int sandf = 0; /* send failure dropping packet */ IPQ_SET_VERDICT(q_pckt.packet_id, NF_DROP); /* we fail to send the packet so we free packet related to current */ /* search and destroy packet by packet_id */ sandf = psearch_and_destroy(q_pckt.packet_id, &(q_pckt.mark)); if (!sandf) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "Packet could not be removed: %u", q_pckt.packet_id); } } } return 1; }
/** * Send an authentication request to NuAuth. May restart TLS session * and/or open TLS connection (if closed). * * Create the thread authsrv() when opening a new session. * * Packet maximum size is 512 bytes, * and it's structure is ::nufw_to_nuauth_auth_message_t. * * \param type Type of request (::AUTH_REQUEST, ::AUTH_CONTROL, ...) * \param pckt_data A pointer to a queued_pckt:: holding packet information * \return If an error occurs returns 0, else return 1. */ int auth_request_send(uint8_t type, struct queued_pckt *pckt_data) { unsigned char data[512]; nuv4_nufw_to_nuauth_auth_message_t *msg_header = (nuv4_nufw_to_nuauth_auth_message_t *) & data; unsigned char *msg_content = data + sizeof(nuv4_nufw_to_nuauth_auth_message_t); int msg_length; /* Drop non-IPv(4|6) packet */ if ((((struct iphdr *) (pckt_data->payload))->version != 4) && (((struct iphdr *) (pckt_data->payload))->version != 6)) { log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_DEBUG, "Dropping non-IPv4/non-IPv6 packet (version %u)", ((struct iphdr *) (pckt_data->payload))-> version); return 0; } /* Truncate packet content if needed */ if (sizeof(data) < sizeof(nuv4_nufw_to_nuauth_auth_message_t) + pckt_data->payload_len) { debug_log_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_DEBUG, "Very long packet: truncating!"); pckt_data->payload_len = sizeof(data) - sizeof(nuv4_nufw_to_nuauth_auth_message_t); } msg_length = sizeof(nuv4_nufw_to_nuauth_auth_message_t) + pckt_data->payload_len; /* Fill message header */ msg_header->protocol_version = PROTO_NUFW_VERSION; msg_header->msg_type = type; msg_header->msg_length = htons(msg_length); msg_header->packet_id = htonl(pckt_data->packet_id); msg_header->timestamp = htonl(pckt_data->timestamp); /* Add info about interfaces */ msg_header->mark = pckt_data->mark; memcpy(msg_header->indev, pckt_data->indev, IFNAMSIZ * sizeof(char)); memcpy(msg_header->outdev, pckt_data->outdev, IFNAMSIZ * sizeof(char)); memcpy(msg_header->physindev, pckt_data->physindev, IFNAMSIZ * sizeof(char)); memcpy(msg_header->physoutdev, pckt_data->physoutdev, IFNAMSIZ * sizeof(char)); /* Copy (maybe truncated) packet content */ memcpy(msg_content, pckt_data->payload, pckt_data->payload_len); /* Display message */ log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_DEBUG, "Sending request for %lu", (long)pckt_data->packet_id); /* negotiate TLS connection if needed */ if (!tls.session) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_INFO, "Not connected, trying TLS connection"); tls_connect(); if (tls.session) { int fd; char buf[256]; buf[0] = '\0'; nussl_session_get_cipher(tls.session, buf, sizeof(buf)); log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "[+] TLS connection to nuauth restored (%s:%d), cipher is %s", authreq_addr, authreq_port, (buf[0] != '\0') ? buf : "none" ); fd = nussl_session_get_fd(tls.session); if (fd >= 0) { ev_io_init(&tls.ev_io, tls_activity_cb, nussl_session_get_fd(tls.session), EV_READ); tls.ev_io.data = &nufw_nfq_watcher; ev_io_start(nufw_loop, &tls.ev_io); } } else { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "[!] TLS connection to nuauth can NOT be restored (%s:%d)", authreq_addr, authreq_port); return 0; } } if (nussl_write(tls.session, (char*)data, msg_length) < 0) { debug_log_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_DEBUG, "Error during nussl_write (auth_request_send)."); shutdown_tls(); log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "[!] TLS send failure"); return 0; } return 1; }
/** * \brief Packet server thread function. * * Connect to netfilter to ask a netlink. Read packet * on this link. Check if packet useful for NuFW. If yes, add it to packet * list and/or send it to NuAuth. * * When using NetFilter queue, it uses treat_packet() as callback. * In ipq mode it uses an internal packet parser and process mechanism. * * \return NULL */ void *packetsrv(void *void_arg) { int fatal_error = 0; ev_io iface_watcher; ev_timer timer; int fd; #ifdef HAVE_NFQ_INDEV_NAME struct nlif_handle *nlif_handle; int if_fd; #endif #ifdef HAVE_NFQ_INDEV_NAME nlif_handle = iface_table_open(); if (!nlif_handle) exit(EXIT_FAILURE); if_fd = nlif_fd(nlif_handle); if (if_fd < 0) { exit(EXIT_FAILURE); } fd = packetsrv_open((void *) nlif_handle); #else fd = packetsrv_open(NULL); #endif if (fd < 0) { exit(EXIT_FAILURE); } log_area_printf(DEBUG_AREA_MAIN | DEBUG_AREA_PACKET, DEBUG_LEVEL_DEBUG, "[+] Packet server started"); nufw_loop = ev_loop_new(0); /* add io for nfq */ ev_io_init(&nufw_nfq_watcher , packetsrv_activity_cb, fd, EV_READ); nufw_nfq_watcher.data = nlif_handle; ev_io_start(nufw_loop, &nufw_nfq_watcher); #ifdef HAVE_NFQ_INDEV_NAME /* add io for iface */ ev_io_init(&iface_watcher , iface_activity_cb, if_fd, EV_READ); iface_watcher.data = nlif_handle; ev_io_start(nufw_loop, &iface_watcher); #endif fd = nussl_session_get_fd(tls.session); if (fd >= 0) { ev_io_init(&tls.ev_io, tls_activity_cb, nussl_session_get_fd(tls.session), EV_READ); tls.ev_io.data = &nufw_nfq_watcher; ev_io_start(nufw_loop, &tls.ev_io); } p_pckt_rx = 0; p_pckt_tx = 0; ev_timer_init(&timer, cleaning_timer_cb, 0, 1.0 * CLEANING_DELAY); timer.data = &nufw_nfq_watcher; ev_timer_start(nufw_loop, &timer); /* start loop */ ev_loop(nufw_loop, 0); ev_loop_destroy(nufw_loop); #ifdef HAVE_NFQ_INDEV_NAME iface_table_close(nlif_handle); #endif packetsrv_close(!fatal_error); log_area_printf(DEBUG_AREA_MAIN | DEBUG_AREA_PACKET, DEBUG_LEVEL_WARNING, "[+] Leave packet server thread"); return NULL; }
/** * Process NuAuth message of type #AUTH_ANSWER */ int auth_process_answer(char *dgram, int dgram_size) { nuv5_nuauth_decision_response_t *answer; uint32_t nfmark; int sandf; u_int32_t packet_id; int payload_len; int msg_len; /* check packet size */ if (dgram_size < (int) sizeof(nuv4_nuauth_decision_response_t)) { return -1; } answer = (nuv5_nuauth_decision_response_t *) dgram; /* check payload length */ payload_len = ntohs(answer->payload_len); msg_len = payload_len + sizeof(nuv5_nuauth_decision_response_t); if (dgram_size < msg_len) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "[!] Packet with improper size: payload of %d, received %d (vs %d)", payload_len, dgram_size, (int) (sizeof(nuv5_nuauth_decision_response_t) + payload_len)); return -1; } /* get packet id and user id */ packet_id = ntohl(answer->packet_id); /* search and destroy packet by packet_id */ sandf = psearch_and_destroy(packet_id, &nfmark); if (!sandf) { log_area_printf(DEBUG_AREA_GW | DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "[!] Packet without a known ID: %u", packet_id); return -1; } switch (answer->decision) { case DECISION_ACCEPT: /* accept packet */ debug_log_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_VERBOSE_DEBUG, "(*) Accepting packet with id=%u", packet_id); if (nufw_set_mark) { debug_log_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_VERBOSE_DEBUG, "(*) Marking packet with %d", ntohl(answer->tcmark)); #if HAVE_NFQ_MARK_EXPTIME if (ntohl(answer->expiration) != -1) { IPQ_SET_VWMARK_EXPTIME(packet_id, NF_ACCEPT, answer->tcmark, ntohl(answer->expiration)); } else { IPQ_SET_VWMARK(packet_id, NF_ACCEPT, answer->tcmark); } #else IPQ_SET_VWMARK(packet_id, NF_ACCEPT, answer->tcmark); #endif } else { IPQ_SET_VERDICT(packet_id, NF_ACCEPT); } pckt_tx++; break; case DECISION_REJECT: /* Packet is rejected, ie. dropped and ICMP signalized */ log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_VERBOSE_DEBUG, "(*) Rejecting %" PRIu32, packet_id); IPQ_SET_VERDICT(packet_id, NF_DROP); if (send_icmp_unreach(dgram + sizeof(nuv5_nuauth_decision_response_t), payload_len) == -1) { log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_WARNING, "(*) Could not sent ICMP reject for %" PRIu32, packet_id); } break; default: /* drop packet */ debug_log_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_VERBOSE_DEBUG, "(*) Drop packet %u", packet_id); IPQ_SET_VERDICT(packet_id, NF_DROP); } return msg_len; }
/** * Create a TLS connection to NuAuth: create a TCP socket and connect * to NuAuth using ::adr_srv. * * If x509 is enable (USE_X509 equals to 1), create credentials and check * NuAuth's one. This function modify the tls variable and in particular * set tls.session. * */ void tls_connect() { int ret; ufwissl_session* sess; tls.session = NULL; if (authreq_addr[0] == '/') return tls_connect_unix(); if (!init_x509_filenames()) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_DEBUG, "Couldn't malloc for key or cert filename!"); return; } sess = ufwissl_session_create(UFWISSL_SSL_CTX_CLIENT); if (!sess) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_FATAL, "Unable to create NuSSL session: %s", ufwissl_get_error(sess)); return; } log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_FATAL, "Loading certificate:%s", cert_file); log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_FATAL, "Loading key:%s", key_file); ret = ufwissl_ssl_set_keypair(sess, cert_file, key_file); if (ret != UFWISSL_OK) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_FATAL, "TLS: can not set ufwissl certificate or keyfile: %s", ufwissl_get_error(sess)); ufwissl_session_destroy(sess); return; } /* sets the trusted CA file */ if (ca_file) { ret = ufwissl_ssl_trust_cert_file(sess, ca_file); if (ret != UFWISSL_OK) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_FATAL, "TLS: can not set ufwissl CA file: %s", ufwissl_get_error(sess)); ufwissl_session_destroy(sess); return; } } /* sets the CRL */ if (crl_file) { ret = ufwissl_ssl_set_crl_file(sess, crl_file, ca_file); if (ret != UFWISSL_OK) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_FATAL, "TLS: can not set ufwissl CRL file: %s", ufwissl_get_error(sess)); ufwissl_session_destroy(sess); return; } } ufwissl_set_hostinfo(sess, authreq_addr, authreq_port); ufwissl_set_read_timeout(sess, 0); if (!nufw_strict_tls) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: disabling certificate verification, as asked."); ufwissl_ssl_disable_certificate_check(sess, 1); } if (!nufw_fqdn_check) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: disabling FQDN verification, as asked."); ufwissl_set_session_flag(sess, UFWISSL_SESSFLAG_IGNORE_ID_MISMATCH, 1); } if (ufwissl_open_connection(sess) != UFWISSL_OK) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: cannot connect to tls_socket (%s)", ufwissl_get_error(sess)); ufwissl_session_destroy(sess); return; } #ifdef XXX if (ca_file) { if (nuauth_cert_dn) { if (!check_nuauth_cert_dn(tls_session)) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "TLS: Cannot check the certificate DN"); return; } } } #endif tls.session = sess; }
unsigned int check_nuauth_cert_dn(gnutls_session *tls_session) { /* we check that dn provided in nuauth certificate is valid */ char dn[128]; size_t size; int ret; #if 0 unsigned int algo, bits; time_t expiration_time, activation_time; #endif const gnutls_datum *cert_list; unsigned int cert_list_size = 0; gnutls_x509_crt cert; /* This function only works for X.509 certificates. */ if (gnutls_certificate_type_get(*tls_session) != GNUTLS_CRT_X509) return 0; cert_list = gnutls_certificate_get_peers(*tls_session, &cert_list_size); if (cert_list_size == 0) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: cannot get the peer certificate"); return 1; } /* we only print information about the first certificate */ ret = gnutls_x509_crt_init(&cert); if (ret != 0) { log_area_printf (DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: cannot init x509 cert: %s", gnutls_strerror(ret)); return 0; } ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); if (ret != 0) { log_area_printf (DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: cannot import x509 cert: %s", gnutls_strerror(ret)); return 0; } /* TODO: verify date */ #if 0 expiration_time = gnutls_x509_crt_get_expiration_time(cert); activation_time = gnutls_x509_crt_get_activation_time(cert); /* Extract some of the public key algorithm's parameters */ algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits); #endif size = sizeof(dn); ret = gnutls_x509_crt_get_dn(cert, dn, &size); if (ret != 0) { log_area_printf (DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: cannot copy x509 cert name into buffer: %s", gnutls_strerror(ret)); return 0; } dn[sizeof(dn)-1] = 0; if (strcmp(dn, nuauth_cert_dn)) { log_area_printf (DEBUG_AREA_MAIN, DEBUG_LEVEL_WARNING, "TLS: bad certificate DN received from nuauth server: %s", dn); return 0; } return 1; }
/** * Thread waiting to authentication server (NuAuth) answer. * Call auth_packet_to_decision() on new packet. */ int authsrv(void *data) { int ret; int read_size; char cdgram[512]; char *dgram = cdgram; int offset = 0; int i = 0; log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_VERBOSE_DEBUG, "[+] In auth server thread"); if (tls.session) { read_size = sizeof(nuv5_nuauth_decision_response_t); /* read size of data */ do { if (i>0) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_VERBOSE_DEBUG, "Reading header (pass %d)", i); } ret = ufwissl_read(tls.session, dgram + offset, read_size - offset); if (ret < 0) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "Unable to read header"); if (!strcmp("Resource temporarily unavailable", ufwissl_get_error(tls.session))) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "Resource temporarily unavailable"); i++; continue; } else { close_tls_session(); return NU_EXIT_ERROR; } } else if (ret == 0) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "Disconnect during read"); close_tls_session(); return NU_EXIT_ERROR; } else if (ret != (read_size - offset)) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "Under read: %d for %d", ret, read_size); offset += ret; i++; continue; } else { offset += ret; break; } } while ((offset != read_size) && i < 3); if (i == 3) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "Unable to ufwissl_read %d from session", read_size); return NU_EXIT_ERROR; } read_size = ntohs(((nuv5_nuauth_decision_response_t *) dgram)->payload_len); if (read_size != 0) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_VERBOSE_DEBUG, "going to ufwissl_read: %d", read_size); if (read_size + offset > (int) sizeof(cdgram)) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_VERBOSE_DEBUG, "too big to read ufwissl_read: %d", read_size); close_tls_session(); return NU_EXIT_ERROR; } ret = ufwissl_read(tls.session, dgram + offset, read_size); if (ret != read_size) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_WARNING, "Unable to read data"); return NU_EXIT_ERROR; } } } else ret = 0; if (ret == UFWISSL_SOCK_TIMEOUT) { return NU_EXIT_ERROR; } if (ret <= 0) { log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_VERBOSE_DEBUG, "Error during ufwissl_read: %s", ufwissl_get_error(tls.session)); close_tls_session(); return NU_EXIT_ERROR; } else { ret = read_size + offset; do { read_size = auth_packet_to_decision(dgram, ret); ret -= read_size; dgram = dgram + read_size; } while (ret > 0 && (read_size != -1)); } dgram = cdgram; return NU_EXIT_OK; }
/** * Open a netlink connection and returns file descriptor */ int packetsrv_open(void *data) { int ret; log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_DEBUG, "Opening netfilter queue socket"); log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_DEBUG, "[!] Don't forget to load kernel modules nfnetlink and nfnetlink_queue (using modprobe command)"); /* opening library handle */ h = nfq_open(); if (!h) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "[!] Error during nfq_open()"); return -1; } /* unbinding existing nf_queue handler for AF_INET (if any) */ /* ignoring return, see http://www.spinics.net/lists/netfilter/msg42063.html */ nfq_unbind_pf(h, AF_INET); /* binding nfnetlink_queue as nf_queue handler for AF_INET */ if (nfq_bind_pf(h, AF_INET) < 0) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "[!] Error during nfq_bind_pf()"); return -1; } if (!nufw_no_ipv6) { /* unbinding existing nf_queue handler for AF_INET6 (if any) */ nfq_unbind_pf(h, AF_INET6); /* binding nfnetlink_queue as nf_queue handler for AF_INET6 */ if (nfq_bind_pf(h, AF_INET6) < 0) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "[!] Error during nfq_bind_pf()"); log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "Maybe you need to compile NF_NETLINK* kernel options as modules (not built in the kernel!)"); return -1; } } ret = nfnl_rcvbufsiz(nfq_nfnlh(h), 10 * 65536); log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "rcv buf size set to %d (%d asked)", ret, 10 * 65536); /* binding this socket to queue number ::nfqueue_num * and install our packet handler */ log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_DEBUG, "[+] Binding to netfilter queue %d", nfqueue_num); hndl = nfq_create_queue(h, nfqueue_num, (nfq_callback *) & treat_packet, data); if (!hndl) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "[!] Error during nfq_create_queue() (queue %d busy ?)", nfqueue_num); return -1; } /* setting copy_packet mode */ if (nfq_set_mode(hndl, NFQNL_COPY_PACKET, 0xffff) < 0) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "[!] Can't set packet_copy mode"); return -1; } #ifdef HAVE_NFQ_SET_QUEUE_MAXLEN /* setting queue length */ if (queue_maxlen) { if (nfq_set_queue_maxlen(hndl, queue_maxlen) < 0) { if (nufw_set_mark) { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "[!] Can't set queue length, and mark will be set, leaving !"); exit(EXIT_FAILURE); } else { log_area_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_CRITICAL, "[!] Can't set queue length, continuing anyway"); } } } #endif return nfq_fd(h); }