RADIUS server: Add option for storing log information to SQLite DB
authorJouni Malinen <jouni@qca.qualcomm.com>
Fri, 28 Feb 2014 12:11:13 +0000 (14:11 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 9 Mar 2014 16:21:13 +0000 (18:21 +0200)
If eap_user_file is configured to point to an SQLite database, RADIUS
server code can use that database for log information.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
hostapd/hostapd.eap_user_sqlite
src/ap/authsrv.c
src/radius/radius_server.c
src/radius/radius_server.h

index 2c1f130..826db34 100644 (file)
@@ -16,3 +16,11 @@ INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 use
 
 INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS');
 INSERT INTO wildcards(identity,methods) VALUES ('0','AKA');
+
+CREATE TABLE authlog(
+       timestamp TEXT,
+       session TEXT,
+       nas_ip TEXT,
+       username TEXT,
+       note TEXT
+);
index 8b922ec..6e3decd 100644 (file)
@@ -115,6 +115,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
        srv.eap_req_id_text_len = conf->eap_req_id_text_len;
        srv.pwd_group = conf->pwd_group;
        srv.server_id = conf->server_id ? conf->server_id : "hostapd";
+       srv.sqlite_file = conf->eap_user_sqlite;
 #ifdef CONFIG_RADIUS_TEST
        srv.dump_msk_file = conf->dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
index dd96b59..e0dbdf1 100644 (file)
@@ -8,6 +8,9 @@
 
 #include "includes.h"
 #include <net/if.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
 
 #include "common.h"
 #include "radius.h"
@@ -69,6 +72,8 @@ struct radius_session {
        unsigned int sess_id;
        struct eap_sm *eap;
        struct eap_eapol_interface *eap_if;
+       char *username; /* from User-Name attribute */
+       char *nas_ip;
 
        struct radius_msg *last_msg;
        char *last_from_addr;
@@ -315,6 +320,10 @@ struct radius_server_data {
 
        char *subscr_remediation_url;
        u8 subscr_remediation_method;
+
+#ifdef CONFIG_SQLITE
+       sqlite3 *db;
+#endif /* CONFIG_SQLITE */
 };
 
 
@@ -332,6 +341,52 @@ static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
 static void radius_server_session_remove_timeout(void *eloop_ctx,
                                                 void *timeout_ctx);
 
+void srv_log(struct radius_session *sess, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+void srv_log(struct radius_session *sess, const char *fmt, ...)
+{
+       va_list ap;
+       char *buf;
+       int buflen;
+
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return;
+       va_start(ap, fmt);
+       vsnprintf(buf, buflen, fmt, ap);
+       va_end(ap);
+
+       RADIUS_DEBUG("[0x%x %s] %s", sess->sess_id, sess->nas_ip, buf);
+
+#ifdef CONFIG_SQLITE
+       if (sess->server->db) {
+               char *sql;
+               sql = sqlite3_mprintf("INSERT INTO authlog"
+                                     "(timestamp,session,nas_ip,username,note)"
+                                     " VALUES ("
+                                     "strftime('%%Y-%%m-%%d %%H:%%M:%%f',"
+                                     "'now'),%u,%Q,%Q,%Q)",
+                                     sess->sess_id, sess->nas_ip,
+                                     sess->username, buf);
+               if (sql) {
+                       if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
+                                        NULL) != SQLITE_OK) {
+                               RADIUS_ERROR("Failed to add authlog entry into sqlite database: %s",
+                                            sqlite3_errmsg(sess->server->db));
+                       }
+                       sqlite3_free(sql);
+               }
+       }
+#endif /* CONFIG_SQLITE */
+
+       os_free(buf);
+}
+
 
 static struct radius_client *
 radius_server_get_client(struct radius_server_data *data, struct in_addr *addr,
@@ -397,6 +452,8 @@ static void radius_server_session_free(struct radius_server_data *data,
        radius_msg_free(sess->last_msg);
        os_free(sess->last_from_addr);
        radius_msg_free(sess->last_reply);
+       os_free(sess->username);
+       os_free(sess->nas_ip);
        os_free(sess);
        data->num_sess--;
 }
@@ -479,7 +536,7 @@ radius_server_new_session(struct radius_server_data *data,
 static struct radius_session *
 radius_server_get_new_session(struct radius_server_data *data,
                              struct radius_client *client,
-                             struct radius_msg *msg)
+                             struct radius_msg *msg, const char *from_addr)
 {
        u8 *user;
        size_t user_len;
@@ -490,37 +547,45 @@ radius_server_get_new_session(struct radius_server_data *data,
 
        RADIUS_DEBUG("Creating a new session");
 
-       user = os_malloc(256);
-       if (user == NULL) {
-               return NULL;
-       }
-       res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256);
-       if (res < 0 || res > 256) {
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user,
+                                   &user_len, NULL) < 0) {
                RADIUS_DEBUG("Could not get User-Name");
-               os_free(user);
                return NULL;
        }
-       user_len = res;
        RADIUS_DUMP_ASCII("User-Name", user, user_len);
 
        os_memset(&tmp, 0, sizeof(tmp));
        res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp);
        os_free(tmp.password);
-       os_free(user);
 
-       if (res == 0) {
-               RADIUS_DEBUG("Matching user entry found");
-               sess = radius_server_new_session(data, client);
-               if (sess == NULL) {
-                       RADIUS_DEBUG("Failed to create a new session");
-                       return NULL;
-               }
-               sess->accept_attr = tmp.accept_attr;
-       } else {
+       if (res != 0) {
                RADIUS_DEBUG("User-Name not found from user database");
                return NULL;
        }
 
+       RADIUS_DEBUG("Matching user entry found");
+       sess = radius_server_new_session(data, client);
+       if (sess == NULL) {
+               RADIUS_DEBUG("Failed to create a new session");
+               return NULL;
+       }
+       sess->accept_attr = tmp.accept_attr;
+
+       sess->username = os_malloc(user_len * 2 + 1);
+       if (sess->username == NULL) {
+               radius_server_session_free(data, sess);
+               return NULL;
+       }
+       printf_encode(sess->username, user_len * 2 + 1, user, user_len);
+
+       sess->nas_ip = os_strdup(from_addr);
+       if (sess->nas_ip == NULL) {
+               radius_server_session_free(data, sess);
+               return NULL;
+       }
+
+       srv_log(sess, "New session created");
+
        os_memset(&eap_conf, 0, sizeof(eap_conf));
        eap_conf.ssl_ctx = data->ssl_ctx;
        eap_conf.msg_ctx = data->msg_ctx;
@@ -789,7 +854,8 @@ static int radius_server_request(struct radius_server_data *data,
                                     from_addr, from_port);
                return -1;
        } else {
-               sess = radius_server_get_new_session(data, client, msg);
+               sess = radius_server_get_new_session(data, client, msg,
+                                                    from_addr);
                if (sess == NULL) {
                        RADIUS_DEBUG("Could not create a new session");
                        radius_server_reject(data, client, msg, from, fromlen,
@@ -875,6 +941,10 @@ static int radius_server_request(struct radius_server_data *data,
 
        if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
                is_complete = 1;
+       if (sess->eap_if->eapFail)
+               srv_log(sess, "EAP authentication failed");
+       else if (sess->eap_if->eapSuccess)
+               srv_log(sess, "EAP authentication succeeded");
 
        reply = radius_server_encapsulate_eap(data, client, sess, msg);
 
@@ -889,10 +959,12 @@ static int radius_server_request(struct radius_server_data *data,
 
                switch (radius_msg_get_hdr(reply)->code) {
                case RADIUS_CODE_ACCESS_ACCEPT:
+                       srv_log(sess, "Sending Access-Accept");
                        data->counters.access_accepts++;
                        client->counters.access_accepts++;
                        break;
                case RADIUS_CODE_ACCESS_REJECT:
+                       srv_log(sess, "Sending Access-Reject");
                        data->counters.access_rejects++;
                        client->counters.access_rejects++;
                        break;
@@ -1502,6 +1574,17 @@ radius_server_init(struct radius_server_conf *conf)
                        os_strdup(conf->subscr_remediation_url);
        }
 
+#ifdef CONFIG_SQLITE
+       if (conf->sqlite_file) {
+               if (sqlite3_open(conf->sqlite_file, &data->db)) {
+                       RADIUS_ERROR("Could not open SQLite file '%s'",
+                                    conf->sqlite_file);
+                       radius_server_deinit(data);
+                       return NULL;
+               }
+       }
+#endif /* CONFIG_SQLITE */
+
 #ifdef CONFIG_RADIUS_TEST
        if (conf->dump_msk_file)
                data->dump_msk_file = os_strdup(conf->dump_msk_file);
@@ -1589,6 +1672,12 @@ void radius_server_deinit(struct radius_server_data *data)
        os_free(data->dump_msk_file);
 #endif /* CONFIG_RADIUS_TEST */
        os_free(data->subscr_remediation_url);
+
+#ifdef CONFIG_SQLITE
+       if (data->db)
+               sqlite3_close(data->db);
+#endif /* CONFIG_SQLITE */
+
        os_free(data);
 }
 
index e85d009..46ac312 100644 (file)
@@ -40,6 +40,11 @@ struct radius_server_conf {
        char *client_file;
 
        /**
+        * sqlite_file - SQLite database for storing debug log information
+        */
+       const char *sqlite_file;
+
+       /**
         * conf_ctx - Context pointer for callbacks
         *
         * This is used as the ctx argument in get_eap_user() calls.