start of peapv1
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_peap / rlm_eap_peap.c
index 5fb0d46..5bf7c96 100644 (file)
  * Copyright 2006 The FreeRADIUS server project
  */
 
-#include <freeradius-devel/ident.h>
 RCSID("$Id$")
 
 #include "eap_peap.h"
 
 typedef struct rlm_eap_peap_t {
-       /*
-        *      TLS configuration
-        */
-       char    *tls_conf_name;
+       char const *tls_conf_name;              //!< TLS configuration.
        fr_tls_server_conf_t *tls_conf;
+       char const *default_method_name;        //!< Default tunneled EAP type.
+       int default_method;
 
-       /*
-        *      Default tunneled EAP type
-        */
-       char    *default_method_name;
-       int     default_method;
-
-       /*
-        *      Use the reply attributes from the tunneled session in
-        *      the non-tunneled reply to the client.
-        */
-       int     use_tunneled_reply;
-
-       /*
-        *      Use SOME of the request attributes from outside of the
-        *      tunneled session in the tunneled request
-        */
-       int     copy_request_to_tunnel;
+       char const *inner_eap_module;           //!< module name for inner EAP
+       int auth_type_eap;
+       bool use_tunneled_reply;                //!< Use the reply attributes from the tunneled session in
+                                               //!< the non-tunneled reply to the client.
 
+       bool copy_request_to_tunnel;            //!< Use SOME of the request attributes from outside of the
+                                               //!< tunneled session in the tunneled request.
 #ifdef WITH_PROXY
-       /*
-        *      Proxy tunneled session as EAP, or as de-capsulated
-        *      protocol.
-        */
-       int     proxy_tunneled_request_as_eap;
+       bool proxy_tunneled_request_as_eap;     //!< Proxy tunneled session as EAP, or as de-capsulated
+                                               //!< protocol.
 #endif
+       char const *virtual_server;             //!< Virtual server for inner tunnel session.
 
-       /*
-        *      Virtual server for inner tunnel session.
-        */
-       char    *virtual_server;
-
-       /*
-        *      Do we do SoH request?
-        */
-       int     soh;
-       char    *soh_virtual_server;
-
-       /*
-        *      Do we do require a client cert?
-        */
-       int     req_client_cert;
+       bool soh;                               //!< Do we do SoH request?
+       char const *soh_virtual_server;
+       bool req_client_cert;                   //!< Do we do require a client cert?
 } rlm_eap_peap_t;
 
 
 static CONF_PARSER module_config[] = {
-       { "tls", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_peap_t, tls_conf_name), NULL, NULL },
+       { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, tls_conf_name), NULL },
+
+       { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, default_method_name), "mschapv2" },
 
-       { "default_method", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_peap_t, default_method_name), NULL, "mschapv2" },
+       { "inner_eap_module", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, inner_eap_module), NULL },
 
-       { "copy_request_to_tunnel", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, copy_request_to_tunnel), NULL, "no" },
+       { "copy_request_to_tunnel", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, copy_request_to_tunnel), "no" },
 
-       { "use_tunneled_reply", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, use_tunneled_reply), NULL, "no" },
+       { "use_tunneled_reply", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, use_tunneled_reply), "no" },
 
 #ifdef WITH_PROXY
-       { "proxy_tunneled_request_as_eap", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, proxy_tunneled_request_as_eap), NULL, "yes" },
+       { "proxy_tunneled_request_as_eap", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, proxy_tunneled_request_as_eap), "yes" },
 #endif
 
-       { "virtual_server", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_peap_t, virtual_server), NULL, NULL },
+       { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, virtual_server), NULL },
 
-       { "soh", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, soh), NULL, "no" },
+       { "soh", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, soh), "no" },
 
-       { "require_client_cert", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, req_client_cert), NULL, "no" },
+       { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, req_client_cert), "no" },
 
-       { "soh_virtual_server", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_peap_t, soh_virtual_server), NULL, NULL },
+       { "soh_virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, soh_virtual_server), NULL },
 
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
 /*
  *     Attach the module.
  */
-static int eappeap_attach(CONF_SECTION *cs, void **instance)
+static int mod_instantiate(CONF_SECTION *cs, void **instance)
 {
        rlm_eap_peap_t          *inst;
+       DICT_VALUE const        *dv;
 
        *instance = inst = talloc_zero(cs, rlm_eap_peap_t);
        if (!inst) return -1;
@@ -128,13 +95,18 @@ static int eappeap_attach(CONF_SECTION *cs, void **instance)
                return -1;
        }
 
+       if (!inst->virtual_server) {
+               ERROR("rlm_eap_peap: A 'virtual_server' MUST be defined for security");
+               return -1;
+       }
+
        /*
         *      Convert the name to an integer, to make it easier to
         *      handle.
         */
        inst->default_method = eap_name2type(inst->default_method_name);
        if (inst->default_method < 0) {
-               radlog(L_ERR, "rlm_eap_peap: Unknown EAP type %s",
+               ERROR("rlm_eap_peap: Unknown EAP type %s",
                       inst->default_method_name);
                return -1;
        }
@@ -146,39 +118,34 @@ static int eappeap_attach(CONF_SECTION *cs, void **instance)
        inst->tls_conf = eaptls_conf_parse(cs, "tls");
 
        if (!inst->tls_conf) {
-               radlog(L_ERR, "rlm_eap_peap: Failed initializing SSL context");
+               ERROR("rlm_eap_peap: Failed initializing SSL context");
                return -1;
        }
 
-       return 0;
-}
-
-/*
- *     Free the PEAP per-session data
- */
-static void peap_free(void *p)
-{
-       peap_tunnel_t *t = (peap_tunnel_t *) p;
-
-       if (!t) return;
-
-       pairfree(&t->username);
-       pairfree(&t->state);
-       pairfree(&t->accept_vps);
-       pairfree(&t->soh_reply_vps);
+       /*
+        *      Don't expose this if we don't need it.
+        */
+       if (!inst->inner_eap_module) inst->inner_eap_module = "eap";
+
+       dv = dict_valbyname(PW_AUTH_TYPE, 0, inst->inner_eap_module);
+       if (!dv) {
+               WARN("Failed to find 'Auth-Type %s' section in virtual server %s.  The server cannot proxy inner-tunnel EAP packets.",
+                    inst->inner_eap_module, inst->virtual_server);
+       } else {
+               inst->auth_type_eap = dv->value;
+       }
 
-       talloc_free(t);
+       return 0;
 }
 
-
 /*
- *     Free the PEAP per-session data
+ *     Allocate the PEAP per-session data
  */
-static peap_tunnel_t *peap_alloc(rlm_eap_peap_t *inst, eap_handler_t *handler)
+static peap_tunnel_t *peap_alloc(TALLOC_CTX *ctx, rlm_eap_peap_t *inst)
 {
        peap_tunnel_t *t;
 
-       t = talloc_zero(handler, peap_tunnel_t);
+       t = talloc_zero(ctx, peap_tunnel_t);
 
        t->default_method = inst->default_method;
        t->copy_request_to_tunnel = inst->copy_request_to_tunnel;
@@ -197,41 +164,40 @@ static peap_tunnel_t *peap_alloc(rlm_eap_peap_t *inst, eap_handler_t *handler)
 /*
  *     Send an initial eap-tls request to the peer, using the libeap functions.
  */
-static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
+static int mod_session_init(void *type_arg, eap_handler_t *handler)
 {
        int             status;
        tls_session_t   *ssn;
        rlm_eap_peap_t  *inst;
        VALUE_PAIR      *vp;
-       int             client_cert = FALSE;
+       bool            client_cert;
        REQUEST         *request = handler->request;
 
        inst = type_arg;
 
-       handler->tls = TRUE;
-       handler->finished = FALSE;
+       handler->tls = true;
 
        /*
         *      Check if we need a client certificate.
         */
-       client_cert = inst->req_client_cert;
 
        /*
         * EAP-TLS-Require-Client-Cert attribute will override
         * the require_client_cert configuration option.
         */
-       vp = pairfind(handler->request->config_items, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(handler->request->config, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
        if (vp) {
-               client_cert = vp->vp_integer;
+               client_cert = vp->vp_integer ? true : false;
+       } else {
+               client_cert = inst->req_client_cert;
        }
 
-       ssn = eaptls_session(inst->tls_conf, handler, client_cert);
+       ssn = eaptls_session(handler, inst->tls_conf, client_cert);
        if (!ssn) {
                return 0;
        }
 
        handler->opaque = ((void *)ssn);
-       handler->free_opaque = session_free;
 
        /*
         *      Set up type-specific information.
@@ -254,22 +220,24 @@ static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
         *      so rather than hoping the user figures it out,
         *      we force it here.
         */
-       ssn->length_flag = 0;
+       ssn->length_flag = false;
 
        /*
         *      TLS session initialization is over.  Now handle TLS
         *      related handshaking or application data.
         */
        status = eaptls_start(handler->eap_ds, ssn->peap_flag);
-       RDEBUG2("Start returned %d", status);
-       if (status == 0) {
-               return 0;
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
        }
+       if (status == 0) return 0;
 
        /*
         *      The next stage to process the packet.
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        return 1;
 }
@@ -277,7 +245,7 @@ static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
 /*
  *     Do authentication, by letting EAP-TLS do most of the work.
  */
-static int eappeap_authenticate(void *arg, eap_handler_t *handler)
+static int mod_process(void *arg, eap_handler_t *handler)
 {
        int rcode;
        fr_tls_status_t status;
@@ -291,52 +259,59 @@ static int eappeap_authenticate(void *arg, eap_handler_t *handler)
         *      allocate it if it doesn't already exist.
         */
        if (!tls_session->opaque) {
-         peap = tls_session->opaque = peap_alloc(inst, handler);
-               tls_session->free_opaque = peap_free;
+               peap = tls_session->opaque = peap_alloc(tls_session, inst);
+       }
+
+       /*
+        *      Negotiate PEAP versions down.
+        */
+       if ((handler->eap_ds->response->type.data[0] & 0x03) < tls_session->peap_flag) {
+               tls_session->peap_flag = handler->eap_ds->response->type.data[0] & 0x03;
        }
 
        status = eaptls_process(handler);
-       RDEBUG2("eaptls_process returned %d\n", status);
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       }
+
        switch (status) {
-               /*
-                *      EAP-TLS handshake was successful, tell the
-                *      client to keep talking.
-                *
-                *      If this was EAP-TLS, we would just return
-                *      an EAP-TLS-Success packet here.
-                */
+       /*
+        *      EAP-TLS handshake was successful, tell the
+        *      client to keep talking.
+        *
+        *      If this was EAP-TLS, we would just return
+        *      an EAP-TLS-Success packet here.
+        */
        case FR_TLS_SUCCESS:
-               RDEBUG2("FR_TLS_SUCCESS");
                peap->status = PEAP_STATUS_TUNNEL_ESTABLISHED;
                break;
 
+       /*
+        *      The TLS code is still working on the TLS
+        *      exchange, and it's a valid TLS request.
+        *      do nothing.
+        */
+       case FR_TLS_HANDLED:
                /*
-                *      The TLS code is still working on the TLS
-                *      exchange, and it's a valid TLS request.
-                *      do nothing.
+                *      FIXME: If the SSL session is established, grab the state
+                *      and EAP id from the inner tunnel, and update it with
+                *      the expected EAP id!
                 */
-       case FR_TLS_HANDLED:
-         /*
-          *    FIXME: If the SSL session is established, grab the state
-          *    and EAP id from the inner tunnel, and update it with
-          *    the expected EAP id!
-          */
-               RDEBUG2("FR_TLS_HANDLED");
                return 1;
 
-               /*
-                *      Handshake is done, proceed with decoding tunneled
-                *      data.
-                */
+       /*
+        *      Handshake is done, proceed with decoding tunneled
+        *      data.
+        */
        case FR_TLS_OK:
-               RDEBUG2("FR_TLS_OK");
                break;
 
                /*
                 *      Anything else: fail.
                 */
        default:
-               RDEBUG2("FR_TLS_OTHERS");
                return 0;
        }
 
@@ -344,21 +319,20 @@ static int eappeap_authenticate(void *arg, eap_handler_t *handler)
         *      Session is established, proceed with decoding
         *      tunneled data.
         */
-       RDEBUG2("Session established.  Decoding tunneled attributes.");
+       RDEBUG2("Session established.  Decoding tunneled attributes");
 
        /*
         *      We may need PEAP data associated with the session, so
         *      allocate it here, if it wasn't already alloacted.
         */
        if (!tls_session->opaque) {
-         tls_session->opaque = peap_alloc(inst, handler);
-               tls_session->free_opaque = peap_free;
+               tls_session->opaque = peap_alloc(tls_session, inst);
        }
 
        /*
         *      Process the PEAP portion of the request.
         */
-       rcode = eappeap_process(handler, tls_session);
+       rcode = eappeap_process(handler, tls_session, inst->auth_type_eap);
        switch (rcode) {
        case RLM_MODULE_REJECT:
                eaptls_fail(handler, 0);
@@ -376,15 +350,19 @@ static int eappeap_authenticate(void *arg, eap_handler_t *handler)
                peap = tls_session->opaque;
                if (peap->soh_reply_vps) {
                        RDEBUG2("Using saved attributes from the SoH reply");
-                       debug_pair_list(peap->soh_reply_vps);
-                       pairadd(&handler->request->reply->vps, peap->soh_reply_vps);
-                       peap->soh_reply_vps = NULL;
+                       rdebug_pair_list(L_DBG_LVL_2, request, peap->soh_reply_vps, NULL);
+                       fr_pair_list_mcopy_by_num(handler->request->reply,
+                                 &handler->request->reply->vps,
+                                 &peap->soh_reply_vps, 0, 0, TAG_ANY);
                }
                if (peap->accept_vps) {
                        RDEBUG2("Using saved attributes from the original Access-Accept");
-                       debug_pair_list(peap->accept_vps);
-                       pairadd(&handler->request->reply->vps, peap->accept_vps);
-                       peap->accept_vps = NULL;
+                       rdebug_pair_list(L_DBG_LVL_2, request, peap->accept_vps, NULL);
+                       fr_pair_list_mcopy_by_num(handler->request->reply,
+                                 &handler->request->reply->vps,
+                                 &peap->accept_vps, 0, 0, TAG_ANY);
+               } else if (peap->use_tunneled_reply) {
+                       RDEBUG2("No saved attributes in the original Access-Accept");
                }
 
                /*
@@ -403,7 +381,6 @@ static int eappeap_authenticate(void *arg, eap_handler_t *handler)
                rad_assert(handler->request->proxy != NULL);
 #endif
                return 1;
-               break;
 
        default:
                break;
@@ -418,11 +395,10 @@ static int eappeap_authenticate(void *arg, eap_handler_t *handler)
  *     The module name should be the only globally exported symbol.
  *     That is, everything else should be 'static'.
  */
+extern rlm_eap_module_t rlm_eap_peap;
 rlm_eap_module_t rlm_eap_peap = {
-       "eap_peap",
-       eappeap_attach,                 /* attach */
-       eappeap_initiate,               /* Start the initial request */
-       NULL,                           /* authorization */
-       eappeap_authenticate,           /* authentication */
-       NULL                            /* detach */
+       .name           = "eap_peap",
+       .instantiate    = mod_instantiate,      /* Create new submodule instance */
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process           /* Process next round of EAP method */
 };