/* $NetBSD: isakmp_xauth.c,v 1.11.6.2 2009/04/20 13:35:36 tteras Exp $ */ /* Id: isakmp_xauth.c,v 1.38 2006/08/22 18:17:17 manubsd Exp */ /* * Copyright (C) 2004-2005 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> #include <grp.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 <resolv.h> #ifdef HAVE_SHADOW_H #include <shadow.h> #endif #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 "admin.h" #include "privsep.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> struct rad_handle *radius_auth_state = NULL; struct rad_handle *radius_acct_state = NULL; #endif #ifdef HAVE_LIBPAM #include <security/pam_appl.h> static char *PAM_usr = NULL; static char *PAM_pwd = NULL; static int PAM_conv(int, const struct pam_message **, struct pam_response **, void *); static struct pam_conv PAM_chat = { &PAM_conv, NULL }; #endif #ifdef HAVE_LIBLDAP #include "ldap.h" #include <arpa/inet.h> struct xauth_ldap_config xauth_ldap_config; #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; } int 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 -1; } if (xst->status != XAUTHST_REQSENT) { plog(LLV_ERROR, LOCATION, NULL, "Xauth reply while Xauth state is %d\n", xst->status); return -1; } 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 -1; } 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 -1; } 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 = privsep_xauth_login_system(usr, pwd); break; #ifdef HAVE_LIBRADIUS case ISAKMP_CFG_AUTH_RADIUS: res = xauth_login_radius(iph1, usr, pwd); break; #endif #ifdef HAVE_LIBPAM case ISAKMP_CFG_AUTH_PAM: res = privsep_xauth_login_pam(iph1->mode_cfg->port, iph1->remote, usr, pwd); break; #endif #ifdef HAVE_LIBLDAP case ISAKMP_CFG_AUTH_LDAP: res = xauth_login_ldap(iph1, usr, pwd); break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "Unexpected authentication source\n"); res = -1; break; } /* * Optional group authentication */ if (!res && (isakmp_cfg_config.groupcount)) res = group_check(iph1, isakmp_cfg_config.grouplist, isakmp_cfg_config.groupcount); /* * 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"); return xauth_reply(iph1, port, id, res); } /* * 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 { return xauth_reply(iph1, port, id, res); } } return 0; } 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) (void)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; } int 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 -1; } xst->status = XAUTHST_OK; plog(LLV_INFO, LOCATION, NULL, "login succeeded for user \"%s\"\n", usr); xauth_sendstatus(iph1, XAUTH_STATUS_OK, id); return 0; } 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_radius_init(void) { /* For first time use, initialize Radius */ if ((isakmp_cfg_config.authsource == ISAKMP_CFG_AUTH_RADIUS) && (radius_auth_state == NULL)) { if ((radius_auth_state = rad_auth_open()) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot init libradius\n"); return -1; } if (rad_config(radius_auth_state, NULL) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot open librarius config file: %s\n", rad_strerror(radius_auth_state)); rad_close(radius_auth_state); radius_auth_state = NULL; return -1; } } if ((isakmp_cfg_config.accounting == ISAKMP_CFG_ACCT_RADIUS) && (radius_acct_state == NULL)) { if ((radius_acct_state = rad_acct_open()) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot init libradius\n"); return -1; } if (rad_config(radius_acct_state, NULL) != 0) { plog(LLV_ERROR, LOCATION, NULL, "Cannot open librarius config file: %s\n", rad_strerror(radius_acct_state)); rad_close(radius_acct_state); radius_acct_state = NULL; return -1; } } return 0; } int xauth_login_radius(iph1, usr, pwd) struct ph1handle *iph1; char *usr; char *pwd; { int res; const void *data; size_t len; int type; if (rad_create_request(radius_auth_state, RAD_ACCESS_REQUEST) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_create_request failed: %s\n", rad_strerror(radius_auth_state)); return -1; } if (rad_put_string(radius_auth_state, RAD_USER_NAME, usr) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_string failed: %s\n", rad_strerror(radius_auth_state)); return -1; } if (rad_put_string(radius_auth_state, RAD_USER_PASSWORD, pwd) != 0) { plog(LLV_ERROR, LOCATION, NULL, "rad_put_string failed: %s\n", rad_strerror(radius_auth_state)); return -1; } if (isakmp_cfg_radius_common(radius_auth_state, iph1->mode_cfg->port) != 0) return -1; switch (res = rad_send_request(radius_auth_state)) { case RAD_ACCESS_ACCEPT: while ((type = rad_get_attr(radius_auth_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_EXTERN; break; case RAD_FRAMED_IP_NETMASK: iph1->mode_cfg->mask4 = rad_cvt_addr(data); iph1->mode_cfg->flags |= ISAKMP_CFG_MASK4_EXTERN; 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_auth_state)); return -1; break; default: plog(LLV_ERROR, LOCATION, NULL, "rad_send_request returned %d\n", res); return -1; break; } return -1; } #endif #ifdef HAVE_LIBPAM static int PAM_conv(msg_count, msg, rsp, dontcare) int msg_count; const struct pam_message **msg; struct pam_response **rsp; void *dontcare; { int i; int replies = 0; struct pam_response *reply = NULL; if ((reply = racoon_malloc(sizeof(*reply) * msg_count)) == NULL) return PAM_CONV_ERR; bzero(reply, sizeof(*reply) * msg_count); for (i = 0; i < msg_count; i++) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: /* Send the username, libpam frees resp */ reply[i].resp_retcode = PAM_SUCCESS; if ((reply[i].resp = strdup(PAM_usr)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "strdup failed\n"); exit(1); } break; case PAM_PROMPT_ECHO_OFF: /* Send the password, libpam frees resp */ reply[i].resp_retcode = PAM_SUCCESS; if ((reply[i].resp = strdup(PAM_pwd)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "strdup failed\n"); exit(1); } break; case PAM_TEXT_INFO: case PAM_ERROR_MSG: reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = NULL; break; default: if (reply != NULL) racoon_free(reply); return PAM_CONV_ERR; break; } } if (reply != NULL) *rsp = reply; return PAM_SUCCESS; } int xauth_login_pam(port, raddr, usr, pwd) int port; struct sockaddr *raddr; char *usr; char *pwd; { int error; int res; const void *data; size_t len; int type; char *remote = NULL; pam_handle_t *pam = NULL; if (isakmp_cfg_config.port_pool == NULL) { plog(LLV_ERROR, LOCATION, NULL, "isakmp_cfg_config.port_pool == NULL\n"); return -1; } if ((error = pam_start("racoon", usr, &PAM_chat, &isakmp_cfg_config.port_pool[port].pam)) != 0) { if (isakmp_cfg_config.port_pool[port].pam == NULL) { plog(LLV_ERROR, LOCATION, NULL, "pam_start failed\n"); return -1; } else { plog(LLV_ERROR, LOCATION, NULL, "pam_start failed: %s\n", pam_strerror(isakmp_cfg_config.port_pool[port].pam, error)); goto out; } } pam = isakmp_cfg_config.port_pool[port].pam; if ((remote = strdup(saddrwop2str(raddr))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "cannot allocate memory: %s\n", strerror(errno)); goto out; } if ((error = pam_set_item(pam, PAM_RHOST, remote)) != 0) { plog(LLV_ERROR, LOCATION, NULL, "pam_set_item failed: %s\n", pam_strerror(pam, error)); goto out; } PAM_usr = usr; PAM_pwd = pwd; error = pam_authenticate(pam, 0); PAM_usr = NULL; PAM_pwd = NULL; if (error != 0) { plog(LLV_ERROR, LOCATION, NULL, "pam_authenticate failed: %s\n", pam_strerror(pam, error)); goto out; } if ((error = pam_acct_mgmt(pam, 0)) != 0) { plog(LLV_ERROR, LOCATION, NULL, "pam_acct_mgmt failed: %s\n", pam_strerror(pam, error)); goto out; } if ((error = pam_setcred(pam, 0)) != 0) { plog(LLV_ERROR, LOCATION, NULL, "pam_setcred failed: %s\n", pam_strerror(pam, error)); goto out; } if (remote != NULL) free(remote); return 0; out: pam_end(pam, error); isakmp_cfg_config.port_pool[port].pam = NULL; if (remote != NULL) free(remote); return -1; } #endif #ifdef HAVE_LIBLDAP int xauth_ldap_init(void) { int tmplen; int error = -1; xauth_ldap_config.pver = 3; xauth_ldap_config.host = NULL; xauth_ldap_config.port = LDAP_PORT; xauth_ldap_config.base = NULL; xauth_ldap_config.subtree = 0; xauth_ldap_config.bind_dn = NULL; xauth_ldap_config.bind_pw = NULL; xauth_ldap_config.auth_type = LDAP_AUTH_SIMPLE; xauth_ldap_config.attr_user = NULL; xauth_ldap_config.attr_addr = NULL; xauth_ldap_config.attr_mask = NULL; xauth_ldap_config.attr_group = NULL; xauth_ldap_config.attr_member = NULL; /* set default host */ tmplen = strlen(LDAP_DFLT_HOST); xauth_ldap_config.host = vmalloc(tmplen); if (xauth_ldap_config.host == NULL) goto out; memcpy(xauth_ldap_config.host->v, LDAP_DFLT_HOST, tmplen); /* set default user naming attribute */ tmplen = strlen(LDAP_DFLT_USER); xauth_ldap_config.attr_user = vmalloc(tmplen); if (xauth_ldap_config.attr_user == NULL) goto out; memcpy(xauth_ldap_config.attr_user->v, LDAP_DFLT_USER, tmplen); /* set default address attribute */ tmplen = strlen(LDAP_DFLT_ADDR); xauth_ldap_config.attr_addr = vmalloc(tmplen); if (xauth_ldap_config.attr_addr == NULL) goto out; memcpy(xauth_ldap_config.attr_addr->v, LDAP_DFLT_ADDR, tmplen); /* set default netmask attribute */ tmplen = strlen(LDAP_DFLT_MASK); xauth_ldap_config.attr_mask = vmalloc(tmplen); if (xauth_ldap_config.attr_mask == NULL) goto out; memcpy(xauth_ldap_config.attr_mask->v, LDAP_DFLT_MASK, tmplen); /* set default group naming attribute */ tmplen = strlen(LDAP_DFLT_GROUP); xauth_ldap_config.attr_group = vmalloc(tmplen); if (xauth_ldap_config.attr_group == NULL) goto out; memcpy(xauth_ldap_config.attr_group->v, LDAP_DFLT_GROUP, tmplen); /* set default member attribute */ tmplen = strlen(LDAP_DFLT_MEMBER); xauth_ldap_config.attr_member = vmalloc(tmplen); if (xauth_ldap_config.attr_member == NULL) goto out; memcpy(xauth_ldap_config.attr_member->v, LDAP_DFLT_MEMBER, tmplen); error = 0; out: if (error != 0) plog(LLV_ERROR, LOCATION, NULL, "cannot allocate memory\n"); return error; } int xauth_login_ldap(iph1, usr, pwd) struct ph1handle *iph1; char *usr; char *pwd; { int rtn = -1; int res = -1; LDAP *ld = NULL; LDAPMessage *lr = NULL; LDAPMessage *le = NULL; struct berval cred; struct berval **bv = NULL; struct timeval timeout; char *init = NULL; char *filter = NULL; char *atlist[3]; char *basedn = NULL; char *userdn = NULL; int tmplen = 0; int ecount = 0; int scope = LDAP_SCOPE_ONE; atlist[0] = NULL; atlist[1] = NULL; atlist[2] = NULL; /* build our initialization url */ tmplen = strlen("ldap://:") + 17; tmplen += strlen(xauth_ldap_config.host->v); init = racoon_malloc(tmplen); if (init == NULL) { plog(LLV_ERROR, LOCATION, NULL, "unable to alloc ldap init url\n"); goto ldap_end; } sprintf(init,"ldap://%s:%d", xauth_ldap_config.host->v, xauth_ldap_config.port ); /* initialize the ldap handle */ res = ldap_initialize(&ld, init); if (res != LDAP_SUCCESS) { plog(LLV_ERROR, LOCATION, NULL, "ldap_initialize failed: %s\n", ldap_err2string(res)); goto ldap_end; } /* initialize the protocol version */ ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &xauth_ldap_config.pver); /* * attempt to bind to the ldap server. * default to anonymous bind unless a * user dn and password has been * specified in our configuration */ if ((xauth_ldap_config.bind_dn != NULL)&& (xauth_ldap_config.bind_pw != NULL)) { cred.bv_val = xauth_ldap_config.bind_pw->v; cred.bv_len = strlen( cred.bv_val ); res = ldap_sasl_bind_s(ld, xauth_ldap_config.bind_dn->v, NULL, &cred, NULL, NULL, NULL); } else { res = ldap_sasl_bind_s(ld, NULL, NULL, NULL, NULL, NULL, NULL); } if (res!=LDAP_SUCCESS) { plog(LLV_ERROR, LOCATION, NULL, "ldap_sasl_bind_s (search) failed: %s\n", ldap_err2string(res)); goto ldap_end; } /* build an ldap user search filter */ tmplen = strlen(xauth_ldap_config.attr_user->v); tmplen += 1; tmplen += strlen(usr); tmplen += 1; filter = racoon_malloc(tmplen); if (filter == NULL) { plog(LLV_ERROR, LOCATION, NULL, "unable to alloc ldap search filter buffer\n"); goto ldap_end; } sprintf(filter, "%s=%s", xauth_ldap_config.attr_user->v, usr); /* build our return attribute list */ tmplen = strlen(xauth_ldap_config.attr_addr->v) + 1; atlist[0] = racoon_malloc(tmplen); tmplen = strlen(xauth_ldap_config.attr_mask->v) + 1; atlist[1] = racoon_malloc(tmplen); if ((atlist[0] == NULL)||(atlist[1] == NULL)) { plog(LLV_ERROR, LOCATION, NULL, "unable to alloc ldap attrib list buffer\n"); goto ldap_end; } strcpy(atlist[0],xauth_ldap_config.attr_addr->v); strcpy(atlist[1],xauth_ldap_config.attr_mask->v); /* attempt to locate the user dn */ if (xauth_ldap_config.base != NULL) basedn = xauth_ldap_config.base->v; if (xauth_ldap_config.subtree) scope = LDAP_SCOPE_SUBTREE; timeout.tv_sec = 15; timeout.tv_usec = 0; res = ldap_search_ext_s(ld, basedn, scope, filter, atlist, 0, NULL, NULL, &timeout, 2, &lr); if (res != LDAP_SUCCESS) { plog(LLV_ERROR, LOCATION, NULL, "ldap_search_ext_s failed: %s\n", ldap_err2string(res)); goto ldap_end; } /* check the number of ldap entries returned */ ecount = ldap_count_entries(ld, lr); if (ecount < 1) { plog(LLV_WARNING, LOCATION, NULL, "no ldap results for filter \'%s\'\n", filter); goto ldap_end; } if (ecount > 1) { plog(LLV_WARNING, LOCATION, NULL, "multiple (%i) ldap results for filter \'%s\'\n", ecount, filter); } /* obtain the dn from the first result */ le = ldap_first_entry(ld, lr); if (le == NULL) { plog(LLV_ERROR, LOCATION, NULL, "ldap_first_entry failed: invalid entry returned\n"); goto ldap_end; } userdn = ldap_get_dn(ld, le); if (userdn == NULL) { plog(LLV_ERROR, LOCATION, NULL, "ldap_get_dn failed: invalid string returned\n"); goto ldap_end; } /* cache the user dn in the xauth state */ iph1->mode_cfg->xauth.udn = racoon_malloc(strlen(userdn)+1); strcpy(iph1->mode_cfg->xauth.udn,userdn); /* retrieve modecfg address */ bv = ldap_get_values_len(ld, le, xauth_ldap_config.attr_addr->v); if (bv != NULL) { char tmpaddr[16]; /* sanity check for address value */ if ((bv[0]->bv_len < 7)||(bv[0]->bv_len > 15)) { plog(LLV_DEBUG, LOCATION, NULL, "ldap returned invalid modecfg address\n"); ldap_value_free_len(bv); goto ldap_end; } memcpy(tmpaddr,bv[0]->bv_val,bv[0]->bv_len); tmpaddr[bv[0]->bv_len]=0; iph1->mode_cfg->addr4.s_addr = inet_addr(tmpaddr); iph1->mode_cfg->flags |= ISAKMP_CFG_ADDR4_EXTERN; plog(LLV_INFO, LOCATION, NULL, "ldap returned modecfg address %s\n", tmpaddr); ldap_value_free_len(bv); } /* retrieve modecfg netmask */ bv = ldap_get_values_len(ld, le, xauth_ldap_config.attr_mask->v); if (bv != NULL) { char tmpmask[16]; /* sanity check for netmask value */ if ((bv[0]->bv_len < 7)||(bv[0]->bv_len > 15)) { plog(LLV_DEBUG, LOCATION, NULL, "ldap returned invalid modecfg netmask\n"); ldap_value_free_len(bv); goto ldap_end; } memcpy(tmpmask,bv[0]->bv_val,bv[0]->bv_len); tmpmask[bv[0]->bv_len]=0; iph1->mode_cfg->mask4.s_addr = inet_addr(tmpmask); iph1->mode_cfg->flags |= ISAKMP_CFG_MASK4_EXTERN; plog(LLV_INFO, LOCATION, NULL, "ldap returned modecfg netmask %s\n", tmpmask); ldap_value_free_len(bv); } /* * finally, use the dn and the xauth * password to check the users given * credentials by attempting to bind * to the ldap server */ plog(LLV_INFO, LOCATION, NULL, "attempting ldap bind for dn \'%s\'\n", userdn); cred.bv_val = pwd; cred.bv_len = strlen( cred.bv_val ); res = ldap_sasl_bind_s(ld, userdn, NULL, &cred, NULL, NULL, NULL); if(res==LDAP_SUCCESS) rtn = 0; ldap_end: /* free ldap resources */ if (userdn != NULL) ldap_memfree(userdn); if (atlist[0] != NULL) racoon_free(atlist[0]); if (atlist[1] != NULL) racoon_free(atlist[1]); if (filter != NULL) racoon_free(filter); if (lr != NULL) ldap_msgfree(lr); if (init != NULL) racoon_free(init); ldap_unbind_ext_s(ld, NULL, NULL); return rtn; } int xauth_group_ldap(udn, grp) char * udn; char * grp; { int rtn = -1; int res = -1; LDAP *ld = NULL; LDAPMessage *lr = NULL; LDAPMessage *le = NULL; struct berval cred; struct timeval timeout; char *init = NULL; char *filter = NULL; char *basedn = NULL; char *groupdn = NULL; int tmplen = 0; int ecount = 0; int scope = LDAP_SCOPE_ONE; /* build our initialization url */ tmplen = strlen("ldap://:") + 17; tmplen += strlen(xauth_ldap_config.host->v); init = racoon_malloc(tmplen); if (init == NULL) { plog(LLV_ERROR, LOCATION, NULL, "unable to alloc ldap init url\n"); goto ldap_group_end; } sprintf(init,"ldap://%s:%d", xauth_ldap_config.host->v, xauth_ldap_config.port ); /* initialize the ldap handle */ res = ldap_initialize(&ld, init); if (res != LDAP_SUCCESS) { plog(LLV_ERROR, LOCATION, NULL, "ldap_initialize failed: %s\n", ldap_err2string(res)); goto ldap_group_end; } /* initialize the protocol version */ ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &xauth_ldap_config.pver); /* * attempt to bind to the ldap server. * default to anonymous bind unless a * user dn and password has been * specified in our configuration */ if ((xauth_ldap_config.bind_dn != NULL)&& (xauth_ldap_config.bind_pw != NULL)) { cred.bv_val = xauth_ldap_config.bind_pw->v; cred.bv_len = strlen( cred.bv_val ); res = ldap_sasl_bind_s(ld, xauth_ldap_config.bind_dn->v, NULL, &cred, NULL, NULL, NULL); } else { res = ldap_sasl_bind_s(ld, NULL, NULL, NULL, NULL, NULL, NULL); } if (res!=LDAP_SUCCESS) { plog(LLV_ERROR, LOCATION, NULL, "ldap_sasl_bind_s (search) failed: %s\n", ldap_err2string(res)); goto ldap_group_end; } /* build an ldap group search filter */ tmplen = strlen("(&(=)(=))") + 1; tmplen += strlen(xauth_ldap_config.attr_group->v); tmplen += strlen(grp); tmplen += strlen(xauth_ldap_config.attr_member->v); tmplen += strlen(udn); filter = racoon_malloc(tmplen); if (filter == NULL) { plog(LLV_ERROR, LOCATION, NULL, "unable to alloc ldap search filter buffer\n"); goto ldap_group_end; } sprintf(filter, "(&(%s=%s)(%s=%s))", xauth_ldap_config.attr_group->v, grp, xauth_ldap_config.attr_member->v, udn); /* attempt to locate the group dn */ if (xauth_ldap_config.base != NULL) basedn = xauth_ldap_config.base->v; if (xauth_ldap_config.subtree) scope = LDAP_SCOPE_SUBTREE; timeout.tv_sec = 15; timeout.tv_usec = 0; res = ldap_search_ext_s(ld, basedn, scope, filter, NULL, 0, NULL, NULL, &timeout, 2, &lr); if (res != LDAP_SUCCESS) { plog(LLV_ERROR, LOCATION, NULL, "ldap_search_ext_s failed: %s\n", ldap_err2string(res)); goto ldap_group_end; } /* check the number of ldap entries returned */ ecount = ldap_count_entries(ld, lr); if (ecount < 1) { plog(LLV_WARNING, LOCATION, NULL, "no ldap results for filter \'%s\'\n", filter); goto ldap_group_end; } /* success */ rtn = 0; /* obtain the dn from the first result */ le = ldap_first_entry(ld, lr); if (le == NULL) { plog(LLV_ERROR, LOCATION, NULL, "ldap_first_entry failed: invalid entry returned\n"); goto ldap_group_end; } groupdn = ldap_get_dn(ld, le); if (groupdn == NULL) { plog(LLV_ERROR, LOCATION, NULL, "ldap_get_dn failed: invalid string returned\n"); goto ldap_group_end; } plog(LLV_INFO, LOCATION, NULL, "ldap membership group returned \'%s\'\n", groupdn); ldap_group_end: /* free ldap resources */ if (groupdn != NULL) ldap_memfree(groupdn); if (filter != NULL) racoon_free(filter); if (lr != NULL) ldap_msgfree(lr); if (init != NULL) racoon_free(init); ldap_unbind_ext_s(ld, NULL, NULL); return rtn; } #endif #ifndef ANDROID_PATCHED int xauth_login_system(usr, pwd) char *usr; char *pwd; { struct passwd *pw; char *cryptpwd; char *syscryptpwd; #ifdef HAVE_SHADOW_H struct spwd *spw; if ((spw = getspnam(usr)) == NULL) return -1; syscryptpwd = spw->sp_pwdp; #endif if ((pw = getpwnam(usr)) == NULL) return -1; #ifndef HAVE_SHADOW_H syscryptpwd = pw->pw_passwd; #endif /* No root login. Ever. */ if (pw->pw_uid == 0) return -1; if ((cryptpwd = crypt(pwd, syscryptpwd)) == NULL) return -1; if (strcmp(cryptpwd, syscryptpwd) == 0) return 0; return -1; } #endif int xauth_group_system(usr, grp) char * usr; char * grp; { struct group * gr; char * member; int index = 0; gr = getgrnam(grp); if (gr == NULL) { plog(LLV_ERROR, LOCATION, NULL, "the system group name \'%s\' is unknown\n", grp); return -1; } while ((member = gr->gr_mem[index++])!=NULL) { if (!strcmp(member,usr)) { plog(LLV_INFO, LOCATION, NULL, "membership validated\n"); return 0; } } return -1; } int xauth_check(iph1) struct ph1handle *iph1; { struct xauth_state *xst = &iph1->mode_cfg->xauth; /* * Only the server side (edge device) really check for Xauth * status. It does it if the chose authmethod is using Xauth. * On the client side (roadwarrior), we don't check anything. */ switch (AUTHMETHOD(iph1)) { case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R: /* The following are not yet implemented */ case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R: 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; } int group_check(iph1, grp_list, grp_count) struct ph1handle *iph1; char **grp_list; int grp_count; { int res = -1; int grp_index = 0; char * usr = NULL; /* check for presence of modecfg data */ if(iph1->mode_cfg == NULL) { plog(LLV_ERROR, LOCATION, NULL, "xauth group specified but modecfg not found\n"); return res; } /* loop through our group list */ for(; grp_index < grp_count; grp_index++) { /* check for presence of xauth data */ usr = iph1->mode_cfg->xauth.authdata.generic.usr; if(usr == NULL) { plog(LLV_ERROR, LOCATION, NULL, "xauth group specified but xauth not found\n"); return res; } /* call appropriate group validation funtion */ switch (isakmp_cfg_config.groupsource) { case ISAKMP_CFG_GROUP_SYSTEM: res = xauth_group_system( usr, grp_list[grp_index]); break; #ifdef HAVE_LIBLDAP case ISAKMP_CFG_GROUP_LDAP: res = xauth_group_ldap( iph1->mode_cfg->xauth.udn, grp_list[grp_index]); break; #endif default: /* we should never get here */ plog(LLV_ERROR, LOCATION, NULL, "Unknown group auth source\n"); break; } if( !res ) { plog(LLV_INFO, LOCATION, NULL, "user \"%s\" is a member of group \"%s\"\n", usr, grp_list[grp_index]); break; } else { plog(LLV_INFO, LOCATION, NULL, "user \"%s\" is not a member of group \"%s\"\n", usr, grp_list[grp_index]); } } return res; } 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 *mraw = NULL, *mdata; 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->xauth || !iph1->rmconf->xauth->login) { plog(LLV_ERROR, LOCATION, NULL, "Xauth performed " "with no login supplied\n"); return NULL; } dlen = iph1->rmconf->xauth->login->l - 1; iph1->rmconf->xauth->state |= XAUTH_SENT_USERNAME; break; #ifdef ANDROID_PATCHED case XAUTH_PASSCODE: #endif case XAUTH_USER_PASSWORD: if (!iph1->rmconf->xauth || !iph1->rmconf->xauth->login) return NULL; skip = sizeof(struct ipsecdoi_id_b); usr = vmalloc(iph1->rmconf->xauth->login->l - 1 + skip); if (usr == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return NULL; } memset(usr->v, 0, skip); memcpy(usr->v + skip, iph1->rmconf->xauth->login->v, iph1->rmconf->xauth->login->l - 1); if (iph1->rmconf->xauth->pass) { /* A key given through racoonctl */ pwd = iph1->rmconf->xauth->pass; } else { if ((pwd = getpskbyname(usr)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "No password was found for login %s\n", iph1->rmconf->xauth->login->v); vfree(usr); return NULL; } /* We have to free it before returning */ freepwd = 1; } vfree(usr); iph1->rmconf->xauth->state |= XAUTH_SENT_PASSWORD; dlen = pwd->l; break; case XAUTH_MESSAGE: if ((ntohs(attr->type) & ISAKMP_GEN_TV) == 0) { dlen = ntohs(attr->lorv); if (dlen > 0) { mraw = (char*)(attr + 1); mdata = binsanitize(mraw, dlen); if (mdata == NULL) { plog(LLV_ERROR, LOCATION, iph1->remote, "Cannot allocate memory\n"); return NULL; } plog(LLV_NOTIFY,LOCATION, iph1->remote, "XAUTH Message: '%s'.\n", mdata); racoon_free(mdata); } } return NULL; default: plog(LLV_WARNING, LOCATION, NULL, "Ignored attribute %s\n", s_isakmp_cfg_type(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: /* * iph1->rmconf->xauth->login->v is valid, * we just checked it in the previous switch case */ memcpy(data, iph1->rmconf->xauth->login->v, dlen); break; #ifdef ANDROID_PATCHED case XAUTH_PASSCODE: #endif 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; struct xauth_state *xst; size_t dlen = 0; char* mraw = NULL, *mdata; 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: /* * We should only receive ISAKMP mode_cfg SET XAUTH_STATUS * when running as a client (initiator). */ xst = &iph1->mode_cfg->xauth; switch(AUTHMETHOD(iph1)) { case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I: case FICTIVE_AUTH_METHOD_XAUTH_PSKEY_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I: /* Not implemented ... */ case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_I: break; default: plog(LLV_ERROR, LOCATION, NULL, "Unexpected XAUTH_STATUS_OK\n"); return NULL; break; } /* 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; case XAUTH_MESSAGE: if ((ntohs(attr->type) & ISAKMP_GEN_TV) == 0) { dlen = ntohs(attr->lorv); if (dlen > 0) { mraw = (char*)(attr + 1); mdata = binsanitize(mraw, dlen); if (mdata == NULL) { plog(LLV_ERROR, LOCATION, iph1->remote, "Cannot allocate memory\n"); return NULL; } plog(LLV_NOTIFY,LOCATION, iph1->remote, "XAUTH Message: '%s'.\n", mdata); racoon_free(mdata); } } default: plog(LLV_WARNING, LOCATION, NULL, "Ignored attribute %s\n", s_isakmp_cfg_type(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; } #ifdef HAVE_LIBLDAP if (xst->udn != NULL) racoon_free(xst->udn); #endif return; } int xauth_rmconf_used(xauth_rmconf) struct xauth_rmconf **xauth_rmconf; { if (*xauth_rmconf == NULL) { *xauth_rmconf = racoon_malloc(sizeof(**xauth_rmconf)); if (*xauth_rmconf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "xauth_rmconf_used: malloc failed\n"); return -1; } (*xauth_rmconf)->login = NULL; (*xauth_rmconf)->pass = NULL; (*xauth_rmconf)->state = 0; } return 0; } void xauth_rmconf_delete(xauth_rmconf) struct xauth_rmconf **xauth_rmconf; { if (*xauth_rmconf != NULL) { if ((*xauth_rmconf)->login != NULL) vfree((*xauth_rmconf)->login); if ((*xauth_rmconf)->pass != NULL) vfree((*xauth_rmconf)->pass); racoon_free(*xauth_rmconf); *xauth_rmconf = NULL; } return; }