Logo Search packages:      
Sourcecode: ipsec-tools version File versions

isakmp_xauth.c

/* $Id: isakmp_xauth.c,v 1.14 2004/11/30 00:46:09 manubsd Exp $ */

/*
 * Copyright (C) 2004 Emmanuel Dreyfus
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/queue.h>

#include <netinet/in.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#include <netdb.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <ctype.h>

#include "var.h"
#include "misc.h"
#include "vmbuf.h"
#include "plog.h"
#include "sockmisc.h"
#include "schedule.h"
#include "debug.h"

#include "crypto_openssl.h"
#include "isakmp_var.h"
#include "isakmp.h"
#include "evt.h"
#include "handler.h"
#include "throttle.h"
#include "remoteconf.h"
#include "isakmp_inf.h"
#include "isakmp_xauth.h"
#include "isakmp_unity.h"
#include "isakmp_cfg.h"
#include "strnames.h"
#include "ipsec_doi.h"
#include "remoteconf.h"
#include "localconf.h"

#ifdef HAVE_LIBRADIUS
#include <radlib.h>
#endif

void 
xauth_sendreq(iph1)
      struct ph1handle *iph1;
{
      vchar_t *buffer;
      struct isakmp_pl_attr *attr;
      struct isakmp_data *typeattr;
      struct isakmp_data *usrattr;
      struct isakmp_data *pwdattr;
      struct xauth_state *xst = &iph1->mode_cfg->xauth;
      size_t tlen;

      /* Status checks */
      if (iph1->status != PHASE1ST_ESTABLISHED) {
            plog(LLV_ERROR, LOCATION, NULL, 
                "Xauth request while phase 1 is not completed\n");
            return;
      }

      if (xst->status != XAUTHST_NOTYET) {
            plog(LLV_ERROR, LOCATION, NULL, 
                "Xauth request whith Xauth state %d\n", xst->status);
            return;
      }

      plog(LLV_INFO, LOCATION, NULL, "Sending Xauth request\n");

      tlen = sizeof(*attr) +
             + sizeof(*typeattr) +
             + sizeof(*usrattr) +
             + sizeof(*pwdattr);
      
      if ((buffer = vmalloc(tlen)) == NULL) {
            plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate buffer\n");
            return;
      }
      
      attr = (struct isakmp_pl_attr *)buffer->v;
      memset(attr, 0, tlen);

      attr->h.len = htons(tlen);
      attr->type = ISAKMP_CFG_REQUEST;
      attr->id = htons(eay_random());

      typeattr = (struct isakmp_data *)(attr + 1);
      typeattr->type = htons(XAUTH_TYPE | ISAKMP_GEN_TV);
      typeattr->lorv = htons(XAUTH_TYPE_GENERIC);

      usrattr = (struct isakmp_data *)(typeattr + 1);
      usrattr->type = htons(XAUTH_USER_NAME | ISAKMP_GEN_TLV);
      usrattr->lorv = htons(0);

      pwdattr = (struct isakmp_data *)(usrattr + 1);
      pwdattr->type = htons(XAUTH_USER_PASSWORD | ISAKMP_GEN_TLV);
      pwdattr->lorv = htons(0);

      isakmp_cfg_send(iph1, buffer, 
          ISAKMP_NPTYPE_ATTR, ISAKMP_FLAG_E, 1);
      
      vfree(buffer);

      xst->status = XAUTHST_REQSENT;

      return;
}

void 
xauth_attr_reply(iph1, attr, id)
      struct ph1handle *iph1;
      struct isakmp_data *attr;
      int id;
{
      char **outlet = NULL;
      size_t alen = 0;
      int type;
      struct xauth_state *xst = &iph1->mode_cfg->xauth;

      if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) {
            plog(LLV_ERROR, LOCATION, NULL, 
                "Xauth reply but peer did not declare "
                "itself as Xauth capable\n");
            return;
      }

      if (xst->status != XAUTHST_REQSENT) {
            plog(LLV_ERROR, LOCATION, NULL, 
                "Xauth reply while Xauth state is %d\n", xst->status);
            return;
      }

      type = ntohs(attr->type) & ~ISAKMP_GEN_MASK;
      switch (type) {
      case XAUTH_TYPE:
            switch (ntohs(attr->lorv)) {
            case XAUTH_TYPE_GENERIC:
                  xst->authtype = XAUTH_TYPE_GENERIC;
                  break;
            default:
                  plog(LLV_WARNING, LOCATION, NULL, 
                      "Unexpected authentication type %d\n", 
                      ntohs(type));
                  return;
            }
            break;

      case XAUTH_USER_NAME:
            outlet = &xst->authdata.generic.usr;
            break;

      case XAUTH_USER_PASSWORD:
            outlet = &xst->authdata.generic.pwd; 
            break;

      default:
            plog(LLV_WARNING, LOCATION, NULL, 
                "ignored Xauth attribute %d\n", type);
            break;
      }

      if (outlet != NULL) {
            alen = ntohs(attr->lorv);

            if ((*outlet = racoon_malloc(alen + 1)) == NULL) {
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Cannot allocate memory for Xauth Data\n");
                  return;
            }

            memcpy(*outlet, attr + 1, alen);
            (*outlet)[alen] = '\0';
            outlet = NULL;
      }

      
      if ((xst->authdata.generic.usr != NULL) &&
         (xst->authdata.generic.pwd != NULL)) {
            int port;
            int res;
            char *usr = xst->authdata.generic.usr;
            char *pwd = xst->authdata.generic.pwd;
            time_t throttle_delay = 0;

#if 0 /* Real debug, don't do that at home */
            plog(LLV_DEBUG, LOCATION, NULL, 
                "Got username \"%s\", password \"%s\"\n", usr, pwd);
#endif
            strncpy(iph1->mode_cfg->login, usr, LOGINLEN);
            iph1->mode_cfg->login[LOGINLEN] = '\0';

            res = -1;
            if ((port = isakmp_cfg_getport(iph1)) == -1) {
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Port pool depleted\n");
                  goto skip_auth;
            }     

            switch (isakmp_cfg_config.authsource) {
            case ISAKMP_CFG_AUTH_SYSTEM:
                  res = xauth_login_system(iph1, usr, pwd);
                  break;
#ifdef HAVE_LIBRADIUS
            case ISAKMP_CFG_AUTH_RADIUS:
                  res = xauth_login_radius(iph1, usr, pwd);
                  break;
#endif
            default:
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Unexpected authentication source\n");
                  res = -1;
                  break;
            }

            /*
             * On failure, throttle the connexion for the remote host
             * in order to make password attacks more difficult.
             */
            throttle_delay = throttle_host(iph1->remote, res) - time(NULL);
            if (throttle_delay > 0) {
                  char *str;

                  str = saddrwop2str(iph1->remote);

                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Throttling in action for %s: delay %lds\n",
                      str, (unsigned long)throttle_delay);
                  res = -1;
            } else {
                  throttle_delay = 0;
            }

skip_auth:
            if (throttle_delay != 0) {
                  struct xauth_reply_arg *xra;

                  if ((xra = racoon_malloc(sizeof(*xra))) == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL, 
                            "malloc failed, bypass throttling\n");
                        xauth_reply(iph1, port, id, res);
                        return;
                  }

                  /*
                   * We need to store the ph1, but it might have
                   * disapeared when xauth_reply is called, so
                   * store the index instead.
                   */
                  xra->index = iph1->index;
                  xra->port = port;
                  xra->id = id;
                  xra->res = res;
                  sched_new(throttle_delay, xauth_reply_stub, xra);
            } else {
                  xauth_reply(iph1, port, id, res);
            }
      }

      return;
}

void 
xauth_reply_stub(args)
      void *args;
{
      struct xauth_reply_arg *xra = (struct xauth_reply_arg *)args;
      struct ph1handle *iph1;

      if ((iph1 = getph1byindex(&xra->index)) != NULL)
            xauth_reply(iph1, xra->port, xra->id, xra->res);
      else
            plog(LLV_ERROR, LOCATION, NULL, 
                "Delayed Xauth reply: phase 1 no longer exists.\n"); 

      racoon_free(xra);
      return;
}

void 
xauth_reply(iph1, port, id, res)
      struct ph1handle *iph1;
      int port;
      int id;
{
      struct xauth_state *xst = &iph1->mode_cfg->xauth;
      char *usr = xst->authdata.generic.usr;

      if (res != 0) {
            if (port != -1)
                  isakmp_cfg_putport(iph1, port);

            plog(LLV_INFO, LOCATION, NULL, 
                "login failed for user \"%s\"\n", usr);
            
            xauth_sendstatus(iph1, XAUTH_STATUS_FAIL, id);
            xst->status = XAUTHST_NOTYET;

            /* Delete Phase 1 SA */
            if (iph1->status == PHASE1ST_ESTABLISHED)
                  isakmp_info_send_d1(iph1);
            remph1(iph1);
            delph1(iph1);

            return;
      }

      xst->status = XAUTHST_OK;
      plog(LLV_INFO, LOCATION, NULL, 
          "login succeeded for user \"%s\"\n", usr);

      xauth_sendstatus(iph1, XAUTH_STATUS_OK, id);

      return;
}

void
xauth_sendstatus(iph1, status, id)
      struct ph1handle *iph1;
      int status;
      int id;
{
      vchar_t *buffer;
      struct isakmp_pl_attr *attr;
      struct isakmp_data *stattr;
      size_t tlen;

      tlen = sizeof(*attr) +
             + sizeof(*stattr); 
      
      if ((buffer = vmalloc(tlen)) == NULL) {
            plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate buffer\n");
            return;
      }
      
      attr = (struct isakmp_pl_attr *)buffer->v;
      memset(attr, 0, tlen);

      attr->h.len = htons(tlen);
      attr->type = ISAKMP_CFG_SET;
      attr->id = htons(id);

      stattr = (struct isakmp_data *)(attr + 1);
      stattr->type = htons(XAUTH_STATUS | ISAKMP_GEN_TV);
      stattr->lorv = htons(status);

      isakmp_cfg_send(iph1, buffer, 
          ISAKMP_NPTYPE_ATTR, ISAKMP_FLAG_E, 1);
      
      vfree(buffer);

      return;     
}

#ifdef HAVE_LIBRADIUS
int
xauth_login_radius(iph1, usr, pwd)
      struct ph1handle *iph1;
      char *usr;
      char *pwd;
{
      static struct rad_handle *radius_state = NULL;
      int res;
      const void *data;
      size_t len;
      int type;

      /* For first time use, initialize Radius */
      if (radius_state == NULL) {
            if ((radius_state = rad_auth_open()) == NULL) {
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Cannot init librradius\n");
                  return -1;
            }

            if (rad_config(radius_state, NULL) != 0) {
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Cannot open librarius config file: %s\n", 
                      rad_strerror(radius_state));
                  rad_close(radius_state);
                  radius_state = NULL;
                  return -1;
            }
      }

      if (rad_create_request(radius_state, RAD_ACCESS_REQUEST) != 0) {
            plog(LLV_ERROR, LOCATION, NULL, 
                "rad_create_request failed: %s\n", 
                rad_strerror(radius_state));
            return -1;
      }
      
      if (rad_put_string(radius_state, RAD_USER_NAME, usr) != 0) {
            plog(LLV_ERROR, LOCATION, NULL, 
                "rad_put_string failed: %s\n", 
                rad_strerror(radius_state));
            return -1;
      }

      if (rad_put_string(radius_state, RAD_USER_PASSWORD, pwd) != 0) {
            plog(LLV_ERROR, LOCATION, NULL, 
                "rad_put_string failed: %s\n", 
                rad_strerror(radius_state));
            return -1;
      }

      if (isakmp_cfg_radius_common(radius_state, iph1->mode_cfg->port) != 0)
            return -1;

      switch (res = rad_send_request(radius_state)) {
      case RAD_ACCESS_ACCEPT:
            while ((type = rad_get_attr(radius_state, &data, &len)) != 0) {
                  switch (type) {
                  case RAD_FRAMED_IP_ADDRESS:
                        iph1->mode_cfg->addr4 = rad_cvt_addr(data);
                        iph1->mode_cfg->flags 
                            |= ISAKMP_CFG_ADDR4_RADIUS;
                        break;

                  case RAD_FRAMED_IP_NETMASK:
                        iph1->mode_cfg->mask4 = rad_cvt_addr(data);
                        iph1->mode_cfg->flags 
                            |= ISAKMP_CFG_MASK4_RADIUS;
                        break;

                  default:
                        plog(LLV_INFO, LOCATION, NULL,
                            "Unexpected attribute: %d\n", type);
                        break;
                  }
            }

            return 0;
            break;

      case RAD_ACCESS_REJECT:
            return -1;
            break;

      case -1:
            plog(LLV_ERROR, LOCATION, NULL, 
                "rad_send_request failed: %s\n", 
                rad_strerror(radius_state));
            return -1;
            break;
      default:
            plog(LLV_ERROR, LOCATION, NULL, 
                "rad_send_request returned %d\n", res);
            return -1;
            break;
      }

      return -1;
}
#endif

int
xauth_login_system(iph1, usr, pwd)
      struct ph1handle *iph1;
      char *usr;
      char *pwd;
{
      struct passwd *pw;
      char *cryptpwd;

      if ((pw = getpwnam(usr)) == NULL)
            return -1;

      /* No root login. Ever. */
      if (pw->pw_uid == 0)
            return -1;

      if ((cryptpwd = crypt(pwd, pw->pw_passwd)) == NULL)
            return -1;

      if (strcmp(cryptpwd, pw->pw_passwd) == 0)
            return 0;

      return -1;
}

int 
xauth_check(iph1)
      struct ph1handle *iph1;
{
      struct xauth_state *xst = &iph1->mode_cfg->xauth;

      /* If we don't use Xauth, then we pass */
      switch (iph1->approval->authmethod) {
      case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
      case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
      /* The following are not yet implemented */
      case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I:
      case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I:
      case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I:
      case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_I:
      case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_I:
            if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) {
                  plog(LLV_ERROR, LOCATION, NULL,
                      "Hybrid auth negotiated but peer did not "
                      "announced as Xauth capable\n");
                  return -1;
            }

            if (xst->status != XAUTHST_OK) {
                  plog(LLV_ERROR, LOCATION, NULL,
                      "Hybrid auth negotiated but peer did not "
                      "succeed Xauth exchange\n");
                  return -1;
            }

            return 0;
            break;
      default:
            return 0;
            break;
      }

      return 0;
}

vchar_t *
isakmp_xauth_req(iph1, attr)
      struct ph1handle *iph1;
      struct isakmp_data *attr;
{
      int type;
      size_t dlen = 0;
      int ashort = 0;
      int value = 0;
      vchar_t *buffer = NULL;
      char *data;
      vchar_t *usr = NULL;
      vchar_t *pwd = NULL;
      size_t skip = 0;
      int freepwd = 0;

      if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) {
            plog(LLV_ERROR, LOCATION, NULL, 
                "Xauth mode config request but peer "
                "did not declare itself as Xauth capable\n");
            return NULL;
      }

      type = ntohs(attr->type) & ~ISAKMP_GEN_MASK;

      /* Sanity checks */
      switch(type) {
      case XAUTH_TYPE:
            if ((ntohs(attr->type) & ISAKMP_GEN_TV) == 0) {
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Unexpected long XAUTH_TYPE attribute\n");
                  return NULL;
            }
            if (ntohs(attr->lorv) != XAUTH_TYPE_GENERIC) {
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Unsupported Xauth authentication %d\n", 
                      ntohs(attr->lorv));
                  return NULL;
            }
            ashort = 1;
            dlen = 0;
            value = XAUTH_TYPE_GENERIC;
            break;

      case XAUTH_USER_NAME:
            if (iph1->rmconf->idvtype != IDTYPE_LOGIN) {
                  plog(LLV_ERROR, LOCATION, NULL, "Xauth performed "
                      "while identifier is not a login\n");
                  return NULL;
            }

            if (iph1->rmconf->idv == NULL) {
                  plog(LLV_ERROR, LOCATION, NULL, "Xauth performed "
                      "with no login supplied\n");
                  return NULL;
            }

            dlen = iph1->rmconf->idv->l;
            break;

      case XAUTH_USER_PASSWORD:
            if (iph1->rmconf->idvtype != IDTYPE_LOGIN) 
                  return NULL;

            if (iph1->rmconf->idv == NULL)
                  return NULL;

            skip = sizeof(struct ipsecdoi_id_b);
            if ((usr = vmalloc(iph1->rmconf->idv->l + skip)) == NULL) {
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Cannot allocate memory\n");
                  return NULL;
            }

            memset(usr->v, 0, skip);
            memcpy(usr->v + skip, 
                iph1->rmconf->idv->v, 
                iph1->rmconf->idv->l);

            if (iph1->rmconf->key) {
                  /* A key given through racoonctl */
                  pwd = iph1->rmconf->key;
            } else {
                  if ((pwd = getpskbyname(usr)) == NULL) {
                        plog(LLV_ERROR, LOCATION, NULL, 
                            "No password was found for login %s\n", 
                            iph1->rmconf->idv->v);
                        vfree(usr);
                        return NULL;
                  }
                  /* We have to free it before returning */
                  freepwd = 1;
            }
            vfree(usr);

            dlen = pwd->l;

            break;

      default:
            plog(LLV_WARNING, LOCATION, NULL,
                "Ignored attribute %d\n", type);
            return NULL;
            break;
      }

      if ((buffer = vmalloc(sizeof(*attr) + dlen)) == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                "Cannot allocate memory\n");
            goto out;
      }

      attr = (struct isakmp_data *)buffer->v;
      if (ashort) {
            attr->type = htons(type | ISAKMP_GEN_TV);
            attr->lorv = htons(value);
            goto out;
      }

      attr->type = htons(type | ISAKMP_GEN_TLV);
      attr->lorv = htons(dlen);
      data = (char *)(attr + 1);

      switch(type) {
      case XAUTH_USER_NAME:
            memcpy(data, iph1->rmconf->idv->v, dlen);
            break;
      case XAUTH_USER_PASSWORD:
            memcpy(data, pwd->v, dlen);
            break;
      default:
            break;
      }

out:
      if (freepwd)
            vfree(pwd);

      return buffer;
}

vchar_t *
isakmp_xauth_set(iph1, attr)
      struct ph1handle *iph1;
      struct isakmp_data *attr;
{
      int type;
      vchar_t *buffer = NULL;
      char *data;

      if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) {
            plog(LLV_ERROR, LOCATION, NULL, 
                "Xauth mode config set but peer "
                "did not declare itself as Xauth capable\n");
            return NULL;
      }

      type = ntohs(attr->type) & ~ISAKMP_GEN_MASK;

      switch(type) {
      case XAUTH_STATUS:
            /* If we got a failure, delete iph1 */
            if (ntohs(attr->lorv) != XAUTH_STATUS_OK) {
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "Xauth authentication failed\n");

                  EVT_PUSH(iph1->local, iph1->remote, 
                      EVTT_XAUTH_FAILED, NULL);

                  iph1->mode_cfg->flags &= ISAKMP_CFG_DELETE_PH1;
            } else {
                  EVT_PUSH(iph1->local, 
                      iph1->remote, EVTT_XAUTH_SUCCESS, NULL);
            }


            /* We acknowledge it */
            break;
      default:
            plog(LLV_WARNING, LOCATION, NULL,
                "Ignored attribute %d\n", type);
            return NULL;
            break;
      }

      if ((buffer = vmalloc(sizeof(*attr))) == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                "Cannot allocate memory\n");
            return NULL;
      }

      attr = (struct isakmp_data *)buffer->v;
      attr->type = htons(type | ISAKMP_GEN_TV);
      attr->lorv = htons(0);

      return buffer;
}


void 
xauth_rmstate(xst)
      struct xauth_state *xst;
{
      switch (xst->authtype) {
      case XAUTH_TYPE_GENERIC:
            if (xst->authdata.generic.usr)
                  racoon_free(xst->authdata.generic.usr);

            if (xst->authdata.generic.pwd)
                  racoon_free(xst->authdata.generic.pwd);

            break;

      case XAUTH_TYPE_CHAP:
      case XAUTH_TYPE_OTP:
      case XAUTH_TYPE_SKEY:
            plog(LLV_WARNING, LOCATION, NULL, 
                "Unsupported authtype %d\n", xst->authtype);
            break;

      default:
            plog(LLV_WARNING, LOCATION, NULL, 
                "Unexpected authtype %d\n", xst->authtype);
            break;
      }

      return;
}

Generated by  Doxygen 1.6.0   Back to index