void http_callback_auth(httpd *webserver, request *r) { t_client *client; httpVar * token; char *mac; if ((token = httpdGetVariableByName(r, "token"))) { /* They supplied variable "token" */ if (!(mac = arp_get(r->clientAddr))) { /* We could not get their MAC address */ debug(LOG_ERR, "Failed to retrieve MAC address for ip %s", r->clientAddr); send_http_page(r, "WiFiDog Error", "Failed to retrieve your MAC address"); } else { /* We have their MAC address */ LOCK_CLIENT_LIST(); if ((client = client_list_find(r->clientAddr, mac)) == NULL) { debug(LOG_DEBUG, "New client for %s", r->clientAddr); client_list_append(r->clientAddr, mac, token->value); } else { debug(LOG_DEBUG, "Client for %s is already in the client list", client->ip); } UNLOCK_CLIENT_LIST(); authenticate_client(r); free(mac); } } else { /* They did not supply variable "token" */ send_http_page(r, "WiFiDog error", "Invalid token"); } }
/** * Given an IP address, add a client corresponding to that IP to client list. * Return a pointer to the new client list entry, or to an existing entry * if one with the given IP already exists. * Return NULL if no new client entry can be created. */ t_client * client_list_add_client(const char *ip) { t_client *client; char *mac, *token; s_config *config; if(!check_ip_format(ip)) { /* Inappropriate format in IP address */ debug(LOG_NOTICE, "Illegal IP format [%s]", ip); return NULL; } if (!(mac = arp_get(ip))) { /* We could not get their MAC address */ debug(LOG_NOTICE, "Could not arp MAC address for %s", ip); return NULL; } if ((client = client_list_find(ip, mac)) == NULL) { token = _client_list_make_auth_token(ip,mac); /* get a new token */ client = _client_list_append(ip, mac, token); free(token); } else { debug(LOG_INFO, "Client %s %s token %s already on client list", ip, mac, client->token); } free(mac); return client; }
long get_online_time(const char *ip,const char *mac) { t_client *ptr; long online_time = 0; ptr = client_list_find(ip,mac); if(NULL!= ptr) { online_time = time(NULL) - ptr->record_time; } return online_time; }
void http_callback_auth(httpd * webserver, request * r) { t_client *client; httpVar *token; char *mac; httpVar *logout = httpdGetVariableByName(r, "logout"); const s_config *config = config_get_config(); if ((token = httpdGetVariableByName(r, "token"))) { /* They supplied variable "token" */ if (!(mac = arp_get(r->clientAddr))) { /* We could not get their MAC address */ debug(LOG_ERR, "Failed to retrieve MAC address for ip %s", r->clientAddr); send_http_page(r, "WiFiDog Error", "Failed to retrieve your MAC address"); } else { t_redir_node *node; int index = 0; LOCK_REDIR(); node = redir_list_find(mac); if (node) { index = node->wlindex; } UNLOCK_REDIR(); /* We have their MAC address */ LOCK_CLIENT_LIST(); if ((client = client_list_find(r->clientAddr, mac)) == NULL) { debug(LOG_DEBUG, "New client for %s", r->clientAddr); client = client_list_add(r->clientAddr, mac, token->value); client->fw_connection_state = FW_MARK_REDIR; client->counters.active_duration = config->sessiontimeout[index-1]; } else if (logout) { logout_client(client); } else { debug(LOG_DEBUG, "Client for %s is already in the client list", client->ip); } UNLOCK_CLIENT_LIST(); if (!logout) { /* applies for case 1 and 3 from above if */ authenticate_client(r); } free(mac); } } else { /* They did not supply variable "token" */ send_http_page(r, "WiFiDog error", "Invalid token"); } }
/** * @brief libmicrohttpd_cb called when the client do a request to this server * @param cls unused * @param connection - client connection * @param url - which url was called * @param method - POST / GET / ... * @param version http 1.0 or 1.1 * @param upload_data - unused * @param upload_data_size - unused * @param ptr - unused * @return */ int libmicrohttpd_cb(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { t_client *client; char *ip_addr; char *mac; int ret; debug(LOG_DEBUG, "access: %s %s", method, url); /* only allow get */ if(0 != strcmp(method, "GET")) { debug(LOG_DEBUG, "Unsupported http method %s", method); return send_error(connection, 503); } /* switch between preauth, authenticated */ /* - always - set caching headers * a) possible implementation - redirect first and serve them using a tempo redirect * b) serve direct * should all requests redirected? even those to .css, .js, ... or respond with 404/503/... */ ip_addr = get_ip(connection); mac = arp_get(ip_addr); client = client_list_find(ip_addr, mac); if(client) { if(client->fw_connection_state == FW_MARK_AUTHENTICATED || client->fw_connection_state == FW_MARK_TRUSTED) { /* client already authed - dangerous!!! This should never happen */ ret = authenticated(connection, ip_addr, mac, url, client); free(mac); free(ip_addr); return ret; } } ret = preauthenticated(connection, ip_addr, mac, url, client); free(mac); free(ip_addr); return ret; }
/** Ping clients to see if they are still active, * refresh their traffic counters, * remove and deny them if timed out */ void fw_refresh_client_list(void) { t_client *cp1, *cp2; time_t now, added_time, last_updated; s_config *config = config_get_config(); /* Update all the counters */ if (-1 == iptables_fw_counters_update()) { debug(LOG_ERR, "Could not get counters from firewall!"); return; } LOCK_CLIENT_LIST(); for (cp1 = cp2 = client_get_first_client(); NULL != cp1; cp1 = cp2) { cp2 = cp1->next; if (!(cp1 = client_list_find(cp1->ip, cp1->mac))) { debug(LOG_ERR, "Node %s was freed while being re-validated!", cp1->ip); } else { now = time(NULL); last_updated = cp1->counters.last_updated; added_time = cp1->added_time; if (last_updated + (config->checkinterval * config->clienttimeout) <= now) { /* Timing out inactive user */ debug(LOG_NOTICE, "%s %s inactive %d secs. kB in: %llu kB out: %llu", cp1->ip, cp1->mac, config->checkinterval * config->clienttimeout, cp1->counters.incoming/1000, cp1->counters.outgoing/1000); if (cp1->fw_connection_state == FW_MARK_AUTHENTICATED) { iptables_fw_access(AUTH_MAKE_DEAUTHENTICATED, cp1); } client_list_delete(cp1); } else if (added_time + (config->checkinterval * config->clientforceout) <= now) { /* Forcing out user */ debug(LOG_NOTICE, "%s %s connected %d secs. kB in: %llu kB out: %llu", cp1->ip, cp1->mac, config->checkinterval * config->clientforceout, cp1->counters.incoming/1000, cp1->counters.outgoing/1000); if (cp1->fw_connection_state == FW_MARK_AUTHENTICATED) { iptables_fw_access(AUTH_MAKE_DEAUTHENTICATED, cp1); } client_list_delete(cp1); } } } UNLOCK_CLIENT_LIST(); }
/** Take action on a client. * Alter the firewall rules and client list accordingly. */ void auth_client_action(const char ip[], const char mac[], t_authaction action) { t_client *client; LOCK_CLIENT_LIST(); client = client_list_find(ip,mac); /* Client should already have hit the server and be on the client list */ if (client == NULL) { debug(LOG_ERR, "Client %s %s action %d is not on client list", ip, mac, action); UNLOCK_CLIENT_LIST(); return; } switch(action) { case AUTH_MAKE_AUTHENTICATED: if(client->fw_connection_state != FW_MARK_AUTHENTICATED) { client->fw_connection_state = FW_MARK_AUTHENTICATED; iptables_fw_access(AUTH_MAKE_AUTHENTICATED, client); authenticated_since_start++; } else { debug(LOG_INFO, "Nothing to do, %s %s already authenticated", client->ip, client->mac); } break; case AUTH_MAKE_DEAUTHENTICATED: if(client->fw_connection_state == FW_MARK_AUTHENTICATED) { iptables_fw_access(AUTH_MAKE_DEAUTHENTICATED, client); } client_list_delete(client); break; default: debug(LOG_ERR, "Unknown auth action: %d",action); } UNLOCK_CLIENT_LIST(); return; }
void http_callback_auth(httpd *webserver, request *r) { t_client *client; httpVar * token; char *mac; httpVar *logout = httpdGetVariableByName(r, "logout"); if ((token = httpdGetVariableByName(r, "token"))) { /* They supplied variable "token" */ if (!(mac = arp_get(r->clientAddr))) { /* We could not get their MAC address */ debug(LOG_ERR, "Failed to retrieve MAC address for ip %s", r->clientAddr); send_http_page(r, "WiFiDog Error", "Failed to retrieve your MAC address"); } else { /* We have their MAC address */ LOCK_CLIENT_LIST(); if ((client = client_list_find(r->clientAddr, mac)) == NULL) { debug(LOG_DEBUG, "New client for %s", r->clientAddr); client_list_append(r->clientAddr, mac, token->value); } else if (logout) { t_authresponse authresponse; s_config *config = config_get_config(); unsigned long long incoming = client->counters.incoming; unsigned long long outgoing = client->counters.outgoing; char *ip = safe_strdup(client->ip); char *urlFragment = NULL; t_auth_serv *auth_server = get_auth_server(); fw_deny(client->ip, client->mac, client->fw_connection_state); client_list_delete(client); debug(LOG_DEBUG, "Got logout from %s", client->ip); /* Advertise the logout if we have an auth server */ if (config->auth_servers != NULL) { UNLOCK_CLIENT_LIST(); auth_server_request(&authresponse, REQUEST_TYPE_LOGOUT, ip, mac, token->value, incoming, outgoing); LOCK_CLIENT_LIST(); /* Re-direct them to auth server */ debug(LOG_INFO, "Got manual logout from client ip %s, mac %s, token %s" "- redirecting them to logout message", client->ip, client->mac, client->token); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_ACCOUNT_LOGGED_OUT ); http_send_redirect_to_auth(r, urlFragment, "Redirect to logout message"); free(urlFragment); } free(ip); } else { debug(LOG_DEBUG, "Client for %s is already in the client list", client->ip); } UNLOCK_CLIENT_LIST(); if (!logout) { authenticate_client(r); } free(mac); } } else { /* They did not supply variable "token" */ send_http_page(r, "WiFiDog error", "Invalid token"); } }
/** Perform username/password check if configured to use it. */ int http_nodogsplash_check_userpass(request *r, t_auth_target *authtarget) { s_config *config; t_client *client; config = config_get_config(); int attempts = 0; char *ip; char *mac; if(!config->passwordauth && !config->usernameauth) { /* Not configured to use username/password check; can't fail. */ return 1; } ip = r->clientAddr; if (!(mac = arp_get(ip))) { /* we could not get their MAC address; fail */ debug(LOG_NOTICE, "Could not arp MAC address for %s to check user/password", ip); return 0; } /* We have their MAC address, find them on the client list and increment their password attempt counter */ LOCK_CLIENT_LIST(); client = client_list_find(ip,mac); if(client) attempts = ++(client->attempts); UNLOCK_CLIENT_LIST(); if(!client) { /* not on client list; fail */ debug(LOG_NOTICE, "Client %s %s not on client list to check user/password", ip, mac); free(mac); return 0; } if(attempts > config->passwordattempts) { /* too many attempts; fail */ debug(LOG_NOTICE, "Client %s %s exceeded %d password attempts", ip, mac, config->passwordattempts); free(mac); return 0; } if ((!config->usernameauth || (authtarget->username && !strcmp(config->username,authtarget->username))) && (!config->passwordauth || (authtarget->password && !strcmp(config->password,authtarget->password)))) { /* password and username match; success */ debug(LOG_NOTICE, "Client %s %s username/password '%s'/'%s'", ip, mac, authtarget->username, authtarget->password); free(mac); return 1; } /* fail */ debug(LOG_NOTICE, "Client %s %s bad username/password '%s'/'%s'", ip, mac, authtarget->username, authtarget->password); free(mac); return 0; }
/** The multipurpose authentication action handler */ void http_nodogsplash_callback_action(request *r, t_auth_target *authtarget, t_authaction action) { t_client *client; char *mac; const char *ip; char *clienttoken = NULL; const char *requesttoken = authtarget->token; const char *redir = authtarget->redir; ip = r->clientAddr; if(!requesttoken) { debug(LOG_NOTICE, "No token in request from ip %s", ip); return; } if(!redir) { debug(LOG_NOTICE, "No redirect in request from ip %s", ip); return; } if (!(mac = arp_get(ip))) { /* We could not get their MAC address */ debug(LOG_NOTICE, "Could not arp MAC address for %s action %d", ip, action); return; } /* We have their MAC address, find them on the client list */ LOCK_CLIENT_LIST(); client = client_list_find(ip,mac); if(client && client->token) { clienttoken = safe_strdup(client->token); } UNLOCK_CLIENT_LIST(); if(!client) { debug(LOG_NOTICE, "Client %s %s action %d is not on client list", ip, mac, action); http_nodogsplash_serve_info(r, "Nodogsplash Error", "You are not on the client list."); free(mac); return; } /* We have a client */ /* Do we have a client token? */ if(!clienttoken) { debug(LOG_NOTICE, "Client %s %s action %d does not have a token", ip, mac, action); free(mac); return; } debug(LOG_DEBUG, "Action %d: %s %s tokens %s, %s", action, ip, mac, clienttoken, requesttoken); debug(LOG_DEBUG, "Redirect: %s", redir); /* Check token match */ if (strcmp(clienttoken,requesttoken)) { /* tokens don't match, reject */ debug(LOG_NOTICE, "Client %s %s tokens %s, %s do not match", r->clientAddr, mac, clienttoken, requesttoken); http_nodogsplash_serve_info(r, "Nodogsplash Error", "Tokens do not match."); free(mac); free(clienttoken); return; } /* Log value of info string, if any */ if(authtarget->info) { debug(LOG_NOTICE, "Client %s %s info: %s", ip, mac, authtarget->info); } /* take action */ switch(action) { case AUTH_MAKE_AUTHENTICATED: auth_client_action(ip,mac,action); http_nodogsplash_redirect(r, redir); break; case AUTH_MAKE_DEAUTHENTICATED: auth_client_action(ip,mac,action); http_nodogsplash_serve_info(r, "Nodogsplash Deny", "Authentication revoked."); break; default: debug(LOG_ERR, "Unknown auth action: %d", action); } free(mac); free(clienttoken); return; }
/** Authenticates a single client against the central server and returns when done * Alters the firewall rules depending on what the auth server says @param r httpd request struct */ void authenticate_client(request *r) { t_client *client; t_authresponse auth_response; char *mac, *token; char *urlFragment = NULL; s_config *config = NULL; t_auth_serv *auth_server = NULL; httpVar *click_ad = httpdGetVariableByName(r, "click_ad"); LOCK_CLIENT_LIST(); client = client_list_find_by_ip(r->clientAddr); if (client == NULL) { debug(LOG_ERR, "authenticate_client(): Could not find client for %s", r->clientAddr); UNLOCK_CLIENT_LIST(); return; } mac = safe_strdup(client->mac); token = safe_strdup(client->token); UNLOCK_CLIENT_LIST(); /* * At this point we've released the lock while we do an HTTP request since it could * take multiple seconds to do and the gateway would effectively be frozen if we * kept the lock. */ auth_server_request(&auth_response, REQUEST_TYPE_LOGIN, r->clientAddr, mac, token, 0, 0); LOCK_CLIENT_LIST(); /* can't trust the client to still exist after n seconds have passed */ client = client_list_find(r->clientAddr, mac); if (client == NULL) { debug(LOG_ERR, "authenticate_client(): Could not find client node for %s (%s)", r->clientAddr, mac); UNLOCK_CLIENT_LIST(); free(token); free(mac); return; } free(token); free(mac); /* Prepare some variables we'll need below */ config = config_get_config(); auth_server = get_auth_server(); switch(auth_response.authcode) { case AUTH_ERROR: /* Error talking to central server */ debug(LOG_ERR, "Got %d from central server authenticating token %s from %s at %s", auth_response, client->token, client->ip, client->mac); send_http_page(r, "Error!", "Error: We did not get a valid answer from the central server"); break; case AUTH_DENIED: /* Central server said invalid token */ debug(LOG_INFO, "Got DENIED from central server authenticating token %s from %s at %s - deleting from firewall and redirecting them to denied message", client->token, client->ip, client->mac); fw_deny(client->ip, client->mac, FW_MARK_KNOWN); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_DENIED ); http_send_redirect_to_auth(r, urlFragment, "Redirect to denied message"); free(urlFragment); break; case AUTH_VALIDATION: /* They just got validated for X minutes to check their email */ debug(LOG_INFO, "Got VALIDATION from central server authenticating token %s from %s at %s" "- adding to firewall and redirecting them to activate message", client->token, client->ip, client->mac); client->fw_connection_state = FW_MARK_PROBATION; fw_allow(client->ip, client->mac, FW_MARK_PROBATION); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_ACTIVATE_ACCOUNT ); http_send_redirect_to_auth(r, urlFragment, "Redirect to activate message"); free(urlFragment); break; case AUTH_ALLOWED: /* Logged in successfully as a regular account */ debug(LOG_INFO, "Got ALLOWED from central server authenticating token %s from %s at %s - " "adding to firewall and redirecting them to portal", client->token, client->ip, client->mac); client->fw_connection_state = FW_MARK_KNOWN; fw_allow(client->ip, client->mac, FW_MARK_KNOWN); served_this_session++; if(click_ad != NULL) { safe_asprintf(&urlFragment,"%sgw_id=%s&click_ad=%s", auth_server->authserv_portal_script_path_fragment, config->gw_id, click_ad->value); } else { safe_asprintf(&urlFragment, "%sgw_id=%s", auth_server->authserv_portal_script_path_fragment, config->gw_id ); } http_send_redirect_to_auth(r, urlFragment, "Redirect to portal"); free(urlFragment); break; case AUTH_VALIDATION_FAILED: /* Client had X minutes to validate account by email and didn't = too late */ debug(LOG_INFO, "Got VALIDATION_FAILED from central server authenticating token %s from %s at %s " "- redirecting them to failed_validation message", client->token, client->ip, client->mac); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_ACCOUNT_VALIDATION_FAILED ); http_send_redirect_to_auth(r, urlFragment, "Redirect to failed validation message"); free(urlFragment); break; default: debug(LOG_WARNING, "I don't know what the validation code %d means for token %s from %s at %s - sending error message", auth_response.authcode, client->token, client->ip, client->mac); send_http_page(r, "Internal Error", "We can not validate your request at this time"); break; } UNLOCK_CLIENT_LIST(); return; }
/** Authenticates a single client against the central server and returns when done * Alters the firewall rules depending on what the auth server says @param r httpd request struct */ void authenticate_client(request *r, char *gw_id) { t_client *client; t_authresponse auth_response; char *mac, *token; char *urlFragment = NULL; s_config *config = NULL; t_auth_serv *auth_server = NULL; LOCK_CLIENT_LIST(); client = client_list_find_by_ip(r->clientAddr); if (client == NULL) { debug(LOG_ERR, "Could not find client for %s", r->clientAddr); UNLOCK_CLIENT_LIST(); return; } mac = safe_strdup(client->mac); token = safe_strdup(client->token); UNLOCK_CLIENT_LIST(); /* * At this point we've released the lock while we do an HTTP request since it could * take multiple seconds to do and the gateway would effectively be frozen if we * kept the lock. */ auth_server_request(&auth_response, REQUEST_TYPE_LOGIN, r->clientAddr, mac, token, 0, 0, gw_id); LOCK_CLIENT_LIST(); /* can't trust the client to still exist after n seconds have passed */ client = client_list_find(r->clientAddr, mac); if (client == NULL) { debug(LOG_ERR, "Could not find client node for %s (%s)", r->clientAddr, mac); UNLOCK_CLIENT_LIST(); free(token); free(mac); return; } free(token); free(mac); /* Prepare some variables we'll need below */ config = config_get_config(); auth_server = get_auth_server(); switch(auth_response.authcode) { case AUTH_ERROR: /* Error talking to central server */ debug(LOG_ERR, "Got %d from central server authenticating token %s from %s at %s", auth_response, client->token, client->ip, client->mac); send_http_page(r, "Error!", "Error: We did not get a valid answer from the central server"); break; case AUTH_DENIED: /* Central server said invalid token */ debug(LOG_INFO, "Got DENIED from central server authenticating token %s from %s at %s - redirecting them to denied message", client->token, client->ip, client->mac); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_DENIED ); http_send_redirect_to_auth(r, urlFragment, "Redirect to portal", gw_id); //set connection logged out (xperimental) //snprintf(,client->token, client->ip, client->mac) /*{ char mysqlCmd[256]; sprintf(mysqlCmd,"echo \"DELETE FROM connections WHERE token='%s';\"|mysql -uauthpuppy -pauthpuppydev authpuppy > /tmp/wifidog_deauth_mysql_result",client->token); debug(LOG_WARNING,"issuing this would help: %s",mysqlCmd); //system(mysqlCmd);//kills wifidog somehow *g }*/ free(urlFragment); //for race conditions, remove from firewall fw_deny(client->ip, client->mac, client->fw_connection_state); //experimental delete client from list client_list_delete(client); break; case AUTH_VALIDATION: /* They just got validated for X minutes to check their email */ debug(LOG_INFO, "Got VALIDATION from central server authenticating token %s from %s at %s" "- adding to firewall and redirecting them to activate message", client->token, client->ip, client->mac); client->fw_connection_state = FW_MARK_PROBATION; fw_allow(client->ip, client->mac, FW_MARK_PROBATION); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_ACTIVATE_ACCOUNT ); http_send_redirect_to_auth(r, urlFragment, "Redirect to activate message", gw_id); free(urlFragment); break; case AUTH_ALLOWED: /* Logged in successfully as a regular account */ debug(LOG_INFO, "Got ALLOWED from central server authenticating token %s from %s at %s - " "adding to firewall and redirecting them to portal", client->token, client->ip, client->mac); client->fw_connection_state = FW_MARK_KNOWN; fw_allow(client->ip, client->mac, FW_MARK_KNOWN); served_this_session++; safe_asprintf(&urlFragment, "%sgw_id=%s", auth_server->authserv_portal_script_path_fragment, gw_id ); http_send_redirect_to_auth(r, urlFragment, "Redirect to portal", gw_id); free(urlFragment); break; case AUTH_VALIDATION_FAILED: /* Client had X minutes to validate account by email and didn't = too late */ debug(LOG_INFO, "Got VALIDATION_FAILED from central server authenticating token %s from %s at %s " "- redirecting them to failed_validation message", client->token, client->ip, client->mac); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_ACCOUNT_VALIDATION_FAILED ); http_send_redirect_to_auth(r, urlFragment, "Redirect to failed validation message", gw_id); free(urlFragment); break; default: debug(LOG_WARNING, "I don't know what the validation code %d means for token %s from %s at %s - sending error message", auth_response.authcode, client->token, client->ip, client->mac); send_http_page(r, "Internal Error", "We can not validate your request at this time"); break; } UNLOCK_CLIENT_LIST(); return; }