track TLS cache filename
[freeradius.git] / src / modules / rlm_eap / rlm_eap.c
index 3e9a706..38b6829 100644 (file)
@@ -1,7 +1,8 @@
 /*
  *   This program is is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License, version 2 if the
- *   License as published by the Free Software Foundation.
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or (at
+ *   your option) any later version.
  *
  *   This program is distributed in the hope that it will be useful,
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -29,19 +30,15 @@ RCSID("$Id$")
 
 #include "rlm_eap.h"
 
+#include <sys/stat.h>
+
 static const CONF_PARSER module_config[] = {
-       { "default_eap_type", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_t, default_method_name), NULL, "md5" },
-       { "timer_expire", PW_TYPE_INTEGER,
-         offsetof(rlm_eap_t, timer_limit), NULL, "60"},
-       { "ignore_unknown_eap_types", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_t, ignore_unknown_types), NULL, "no" },
-       { "mod_accounting_username_bug", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_t, mod_accounting_username_bug), NULL, "no" },
-       { "max_sessions", PW_TYPE_INTEGER,
-         offsetof(rlm_eap_t, max_sessions), NULL, "2048"},
-
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_t, default_method_name), "md5" },
+       { "timer_expire", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_t, timer_limit), "60" },
+       { "ignore_unknown_eap_types", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_t, ignore_unknown_types), "no" },
+       { "cisco_accounting_username_bug", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_t, mod_accounting_username_bug), "no" },
+       { "max_sessions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_t, max_sessions), "2048" },
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -55,18 +52,9 @@ static int mod_detach(void *instance)
 
 #ifdef HAVE_PTHREAD_H
        pthread_mutex_destroy(&(inst->session_mutex));
-       if (inst->handler_tree) pthread_mutex_destroy(&(inst->handler_mutex));
 #endif
 
        rbtree_free(inst->session_tree);
-       if (inst->handler_tree) {
-               rbtree_free(inst->handler_tree);
-               /*
-                *  Must be NULL else when nodes are freed they try to
-                *  delete themselves from the tree.
-                */
-               inst->handler_tree = NULL;
-       }
        inst->session_tree = NULL;
        eaplist_free(inst);
 
@@ -105,17 +93,6 @@ static int eap_handler_cmp(void const *a, void const *b)
 
 
 /*
- *     Compare two handler pointers
- */
-static int eap_handler_ptr_cmp(void const *a, void const *b)
-{
-       if (a < b) return -1;
-       if (a > b) return +1;
-       return 0;
-}
-
-
-/*
  * read the config section and load all the eap authentication types present.
  */
 static int mod_instantiate(CONF_SECTION *cs, void *instance)
@@ -138,12 +115,17 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
        inst->xlat_name = cf_section_name2(cs);
        if (!inst->xlat_name) inst->xlat_name = "EAP";
 
+       if (!dict_valbyname(PW_AUTH_TYPE, 0, inst->xlat_name)) {
+               cf_log_err_cs(cs, "Failed to find 'Auth-Type %s' section.  Cannot authenticate users.",
+                             inst->xlat_name);
+               return -1;
+       }
+
        /* Load all the configured EAP-Types */
        num_methods = 0;
        for(scs = cf_subsection_find_next(cs, NULL, NULL);
            scs != NULL;
            scs = cf_subsection_find_next(cs, scs, NULL)) {
-
                char const *name;
 
                name = cf_section_name1(scs);
@@ -151,6 +133,12 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
 
                if (!strcmp(name, TLS_CONFIG_SECTION))  continue;
 
+               /*
+                *      Easier sometimes than commenting out blocks,
+                *      or deleting blocks.
+                */
+               if (!strcmp(name, "disable")) continue;
+
                method = eap_name2type(name);
                if (method == PW_EAP_INVALID) {
                        cf_log_err_cs(cs, "No dictionary definition for EAP method %s", name);
@@ -175,19 +163,24 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
                 *      etc. configurations from eap.conf in order to
                 *      have EAP without the TLS types.
                 */
-               if ((method == PW_EAP_TLS) ||
-                   (method == PW_EAP_TTLS) ||
-                   (method == PW_EAP_PEAP)) {
-                       DEBUG2("rlm_eap (%s): Ignoring EAP method %s because we do not have OpenSSL support",
-                              inst->xlat_name, name);
+               switch (method) {
+               case PW_EAP_TLS:
+               case PW_EAP_TTLS:
+               case PW_EAP_PEAP:
+               case PW_EAP_PWD:
+                       WARN("rlm_eap (%s): Ignoring EAP method %s because we don't have OpenSSL support",
+                            inst->xlat_name, name);
                        continue;
+
+               default:
+                       break;
                }
 #endif
 
                /*
                 *      Load the type.
                 */
-               ret = eap_module_load(inst, &inst->methods[method], method, scs);
+               ret = eap_module_instantiate(inst, &inst->methods[method], method, scs);
 
                (void) talloc_get_type_abort(inst->methods[method], eap_module_t);
 
@@ -231,26 +224,12 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
         *      Lookup sessions in the tree.  We don't free them in
         *      the tree, as that's taken care of elsewhere...
         */
-       inst->session_tree = rbtree_create(eap_handler_cmp, NULL, 0);
+       inst->session_tree = rbtree_create(NULL, eap_handler_cmp, NULL, 0);
        if (!inst->session_tree) {
                ERROR("rlm_eap (%s): Cannot initialize tree", inst->xlat_name);
                return -1;
        }
-
-       if (fr_debug_flag) {
-               inst->handler_tree = rbtree_create(eap_handler_ptr_cmp, NULL, 0);
-               if (!inst->handler_tree) {
-                       ERROR("rlm_eap (%s): Cannot initialize tree", inst->xlat_name);
-                       return -1;
-               }
-
-#ifdef HAVE_PTHREAD_H
-               if (pthread_mutex_init(&(inst->handler_mutex), NULL) < 0) {
-                       ERROR("rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, fr_syserror(errno));
-                       return -1;
-               }
-#endif
-       }
+       fr_link_talloc_ctx_free(inst, inst->session_tree);
 
 #ifdef HAVE_PTHREAD_H
        if (pthread_mutex_init(&(inst->session_mutex), NULL) < 0) {
@@ -276,9 +255,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
 
        inst = (rlm_eap_t *) instance;
 
-       if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
-               REDEBUG("You set 'Auth-Type = EAP' for a request that does "
-                       "not contain an EAP-Message attribute!");
+       if (!fr_pair_find_by_num(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
+               REDEBUG("You set 'Auth-Type = %s' for a request that does "
+                       "not contain an EAP-Message attribute!", inst->xlat_name);
                return RLM_MODULE_INVALID;
        }
 
@@ -287,7 +266,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
         */
        eap_packet = eap_vp2packet(request, request->packet->vps);
        if (!eap_packet) {
-               RERROR("Malformed EAP Message");
+               RERROR("Malformed EAP Message: %s", fr_strerror());
                return RLM_MODULE_FAIL;
        }
 
@@ -322,7 +301,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
        /*
         *      If we're doing horrible tunneling work, remember it.
         */
-       if ((request->log.lvl & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
+       if ((request->options & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
                RDEBUG2("No EAP proxy set.  Not composing EAP");
                /*
                 *      Add the handle to the proxied list, so that we
@@ -362,11 +341,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                 *      Some simple sanity checks.  These should really
                 *      be handled by the radius library...
                 */
-               vp = pairfind(request->proxy->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->proxy->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
                if (vp) {
-                       vp = pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
+                       vp = fr_pair_find_by_num(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
                        if (!vp) {
-                               pairmake(request->proxy,
+                               fr_pair_make(request->proxy,
                                         &request->proxy->vps,
                                         "Message-Authenticator",
                                         NULL, T_OP_EQ);
@@ -378,9 +357,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                 *      set to 127.0.0.1 for tunneled requests, and
                 *      we don't want to tell the world that...
                 */
-               pairdelete(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO, VENDORPEC_FREERADIUS, TAG_ANY);
+               fr_pair_delete_by_num(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO, VENDORPEC_FREERADIUS, TAG_ANY);
 
-               RDEBUG2("Tunneled session will be proxied.  Not doing EAP");
+               RWDEBUG2("Tunneled session will be proxied.  Not doing EAP");
                return RLM_MODULE_HANDLED;
        }
 #endif
@@ -435,21 +414,41 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
        }
 
        /*
+        *      Enable the cached entry on success.
+        */
+       if (handler->eap_ds->request->code == PW_EAP_SUCCESS) {
+               VALUE_PAIR *vp;
+
+               vp = fr_pair_find_by_num(request->state, PW_TLS_CACHE_FILENAME, 0, TAG_ANY);
+               if (vp) (void) chmod(vp->vp_strvalue, S_IRUSR | S_IWUSR);
+       }
+
+       /*
+        *      Disable the cached entry on failure.
+        */
+       if (handler->eap_ds->request->code == PW_EAP_FAILURE) {
+               VALUE_PAIR *vp;
+
+               vp = fr_pair_find_by_num(request->state, PW_TLS_CACHE_FILENAME, 0, TAG_ANY);
+               if (vp) (void) unlink(vp->vp_strvalue);
+       }
+
+       /*
         *      If it's an Access-Accept, RFC 2869, Section 2.3.1
         *      says that we MUST include a User-Name attribute in the
         *      Access-Accept.
         */
-       if ((request->reply->code == PW_CODE_AUTHENTICATION_ACK) &&
+       if ((request->reply->code == PW_CODE_ACCESS_ACCEPT) &&
            request->username) {
                VALUE_PAIR *vp;
 
                /*
                 *      Doesn't exist, add it in.
                 */
-               vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!vp) {
-                       vp = paircopyvp(request->reply, request->username);
-                       pairadd(&request->reply->vps, vp);
+                       vp = fr_pair_copy(request->reply, request->username);
+                       fr_pair_add(&request->reply->vps, vp);
                }
 
                /*
@@ -458,11 +457,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                 */
                if (inst->mod_accounting_username_bug) {
                        char const *old = vp->vp_strvalue;
-                       char *new = talloc_zero_array(vp, char, vp->length + 1);
+                       char *new = talloc_zero_array(vp, char, vp->vp_length + 1);
 
-                       memcpy(new, old, vp->length);
+                       memcpy(new, old, vp->vp_length);
                        vp->vp_strvalue = new;
-                       vp->length++;
+                       vp->vp_length++;
 
                        rad_const_free(old);
                }
@@ -504,7 +503,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         *      We therefore send an EAP Identity request.
         */
        status = eap_start(inst, request);
-       switch(status) {
+       switch (status) {
        case EAP_NOOP:
                return RLM_MODULE_NOOP;
        case EAP_FAIL:
@@ -525,9 +524,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         *      each EAP sub-module to look for handler->request->username,
         *      and to get excited if it doesn't appear.
         */
-       vp = pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY);
-       if ((!vp) || (vp->vp_integer != PW_AUTHTYPE_REJECT)) {
-               vp = pairmake_config("Auth-Type", inst->xlat_name, T_OP_EQ);
+       vp = fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
+       if ((!vp) || (vp->vp_integer != PW_AUTH_TYPE_REJECT)) {
+               vp = pair_make_config("Auth-Type", inst->xlat_name, T_OP_EQ);
                if (!vp) {
                        RDEBUG2("Failed to create Auth-Type %s: %s\n",
                                inst->xlat_name, fr_strerror());
@@ -552,17 +551,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *inst, REQUEST *request)
 {
        size_t          i;
        size_t          len;
+       ssize_t         ret;
        char            *p;
        VALUE_PAIR      *vp;
        eap_handler_t   *handler;
        vp_cursor_t     cursor;
 
        /*
-        *      Just in case the admin lists EAP in post-proxy-type Fail.
-        */
-       if (!request->proxy_reply) return RLM_MODULE_NOOP;
-
-       /*
         *      If there was a handler associated with this request,
         *      then it's a tunneled request which was proxied...
         */
@@ -625,14 +620,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *inst, REQUEST *request)
                 *      says that we MUST include a User-Name attribute in the
                 *      Access-Accept.
                 */
-               if ((request->reply->code == PW_CODE_AUTHENTICATION_ACK) &&
+               if ((request->reply->code == PW_CODE_ACCESS_ACCEPT) &&
                    request->username) {
                        /*
                         *      Doesn't exist, add it in.
                         */
-                       vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
+                       vp = fr_pair_find_by_num(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
                        if (!vp) {
-                               pairmake_reply("User-Name",
+                               pair_make_reply("User-Name",
                                               request->username->vp_strvalue,
                                               T_OP_EQ);
                        }
@@ -679,9 +674,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *inst, REQUEST *request)
        /*
         *      The format is very specific.
         */
-       if (vp->length != (17 + 34)) {
+       if (vp->vp_length != (17 + 34)) {
                RDEBUG2("Cisco-AVPair with leap:session-key has incorrect length %zu: Expected %d",
-                      vp->length, 17 + 34);
+                      vp->vp_length, 17 + 34);
                return RLM_MODULE_NOOP;
        }
 
@@ -690,23 +685,40 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *inst, REQUEST *request)
         *
         *      Note that the session key is *binary*, and therefore
         *      may contain embedded zeros.  So we have to use memdup.
+        *      However, Cisco-AVPair is a "string", so the rest of the
+        *      code assumes that it's terminated by a trailing '\0'.
+        *
+        *      So... be sure to (a) use memdup, and (b) include the last
+        *      zero byte.
         */
        i = 34;
-       p = talloc_memdup(vp, vp->vp_octets, vp->length);
+       p = talloc_memdup(vp, vp->vp_strvalue, vp->vp_length + 1);
        talloc_set_type(p, uint8_t);
-       len = rad_tunnel_pwdecode((uint8_t *)p + 17, &i, request->home_server->secret, request->proxy->vector);
+       ret = rad_tunnel_pwdecode((uint8_t *)p + 17, &i, request->home_server->secret, request->proxy->vector);
+       if (ret < 0) {
+               REDEBUG("Decoding leap:session-key failed");
+               talloc_free(p);
+               return RLM_MODULE_FAIL;
+       }
+       len = i;
 
-       /*
-        *      FIXME: Assert that i == 16.
-        */
+       if (i != 16) {
+               REDEBUG("Decoded key length is incorrect, must be 16 bytes");
+               talloc_free(p);
+               return RLM_MODULE_FAIL;
+       }
 
        /*
         *      Encrypt the session key again, using the request data.
         */
-       rad_tunnel_pwencode(p + 17, &len,
-                           request->client->secret,
-                           request->packet->vector);
-       pairstrsteal(vp, p);
+       ret = rad_tunnel_pwencode(p + 17, &len, request->client->secret, request->packet->vector);
+       if (ret < 0) {
+               REDEBUG("Decoding leap:session-key failed");
+               talloc_free(p);
+               return RLM_MODULE_FAIL;
+       }
+
+       fr_pair_value_strsteal(vp, p);
 
        return RLM_MODULE_UPDATED;
 }
@@ -722,23 +734,23 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        /*
         * Only build a failure message if something previously rejected the request
         */
-       vp = pairfind(request->config_items, PW_POSTAUTHTYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
 
-       if (!vp || (vp->vp_integer != PW_POSTAUTHTYPE_REJECT)) return RLM_MODULE_NOOP;
+       if (!vp || (vp->vp_integer != PW_POST_AUTH_TYPE_REJECT)) return RLM_MODULE_NOOP;
 
-       if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
-               RDEBUG2("Request didn't contain an EAP-Message, not inserting EAP-Failure");
+       if (!fr_pair_find_by_num(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
+               RDEBUG3("Request didn't contain an EAP-Message, not inserting EAP-Failure");
                return RLM_MODULE_NOOP;
        }
 
-       if (pairfind(request->reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
-               RDEBUG2("Reply already contained an EAP-Message, not inserting EAP-Failure");
+       if (fr_pair_find_by_num(request->reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
+               RDEBUG3("Reply already contained an EAP-Message, not inserting EAP-Failure");
                return RLM_MODULE_NOOP;
        }
 
        eap_packet = eap_vp2packet(request, request->packet->vps);
        if (!eap_packet) {
-               RERROR("Malformed EAP Message");
+               RERROR("Malformed EAP Message: %s", fr_strerror());
                return RLM_MODULE_FAIL;
        }
 
@@ -756,9 +768,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         * Make sure there's a message authenticator attribute in the response
         * RADIUS protocol code will calculate the correct value later...
         */
-       vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
        if (!vp) {
-               pairmake_reply("Message-Authenticator", "0x00", T_OP_EQ);
+               pair_make_reply("Message-Authenticator", "0x00", T_OP_EQ);
        }
 
        return RLM_MODULE_UPDATED;
@@ -768,26 +780,20 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
  *     The module name should be the only globally exported symbol.
  *     That is, everything else should be 'static'.
  */
+extern module_t rlm_eap;
 module_t rlm_eap = {
-       RLM_MODULE_INIT,
-       "eap",
-       0,      /* type */
-       sizeof(rlm_eap_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "eap",
+       .inst_size      = sizeof(rlm_eap_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
 #ifdef WITH_PROXY
-               mod_post_proxy,         /* post-proxy */
-#else
-               NULL,
+               [MOD_POST_PROXY]        = mod_post_proxy,
 #endif
-               mod_post_auth           /* post-auth */
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };