iscsi_authglue.c revision f3861e1a2ceec23a5b699c24d814b7775a9e0b52
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2000 by Cisco Systems, Inc. All rights reserved.
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* iSCSI Pseudo HBA Driver
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/random.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/iscsi_protocol.h>
#include <sys/iscsi_authclient.h>
#include <sys/types.h>
#include <iscsitgt_impl.h>
#include "radius.h"
#include "queue.h"
#include "iscsi_sess.h"
#include "target.h"
#define DEFAULT_RADIUS_PORT 1812
boolean_t
persistent_radius_get(iscsi_radius_props_t *radius)
{
Boolean_t bRadiusAccess = False;
char *szRadiusServer = NULL;
char *szRadiusSecret = NULL;
char *szRadiusPort = NULL;
int ret = 0;
struct addrinfo *res;
bzero(radius, sizeof (radius));
radius->r_radius_config_valid = B_FALSE;
/* Load RADIUS access option: enable/disable */
if (tgt_find_value_boolean(main_config, XML_ELEMENT_RAD_ACCESS,
&bRadiusAccess) == False) {
return (B_FALSE);
}
if (bRadiusAccess == False) {
return (B_FALSE);
}
/* Load RADIUS server: ipaddr[:port] */
if (tgt_find_value_str(main_config, XML_ELEMENT_RAD_SERV,
&szRadiusServer) == False) {
return (B_FALSE);
}
szRadiusPort = strchr(szRadiusServer, ':');
if (szRadiusPort == NULL) {
radius->r_port = DEFAULT_RADIUS_PORT;
} else {
radius->r_port = strtoul(szRadiusPort + 1, NULL, 0);
if (radius->r_port == 0) {
radius->r_port = DEFAULT_RADIUS_PORT;
}
*szRadiusPort = '\0';
}
ret = getaddrinfo(szRadiusServer, NULL, NULL, &res);
free(szRadiusServer);
if (ret != 0) {
return (B_FALSE);
}
if (res->ai_family == PF_INET) {
struct sockaddr_in sa_tmp;
bcopy(res->ai_addr, &sa_tmp, sizeof (sa_tmp));
radius->r_insize = sizeof (in_addr_t);
radius->r_addr.u_in4 = sa_tmp.sin_addr;
}
/*
* We don't handle IPV6 currently.
*/
/* Load RADIUS shared secret */
if (tgt_find_value_str(main_config, XML_ELEMENT_RAD_SECRET,
&szRadiusSecret) == False) {
freeaddrinfo(res);
return (B_FALSE);
}
(void) strncpy((char *)radius->r_shared_secret, szRadiusSecret,
MAX_RAD_SHARED_SECRET_LEN);
radius->r_shared_secret_len = strlen((char *)radius->r_shared_secret);
free(szRadiusSecret);
freeaddrinfo(res);
/* Set RADIUS config flag */
radius->r_radius_access = B_TRUE;
radius->r_radius_config_valid = B_TRUE;
return (B_TRUE);
}
/*
* Authenticate a target's CHAP response.
*
* username - Incoming username from the the target.
* responseData - Incoming response data from the target.
*/
int
iscsiAuthClientChapAuthRequest(IscsiAuthClient *client,
char *username, unsigned int id, uchar_t *challengeData,
unsigned int challengeLength, uchar_t *responseData,
unsigned int responseLength)
{
iscsi_sess_t *isp = (iscsi_sess_t *)client->userHandle;
IscsiAuthMd5Context context;
iscsi_radius_props_t p_radius_cfg;
uchar_t verifyData[iscsiAuthChapResponseLength];
char debug[128];
if (isp == NULL) {
return (iscsiAuthStatusFail);
}
/*
* the expected credentials are in the session
*/
if (isp->sess_auth.username_in == NULL) {
(void) snprintf(debug, sizeof (debug),
"SES%x iscsi session(%u) failed authentication, "
"no incoming username configured to authenticate initiator",
isp->s_num);
queue_str(isp->s_mgmtq, Q_SESS_ERRS, msg_log, debug);
return (iscsiAuthStatusFail);
}
if (strcmp(username, isp->sess_auth.username_in) != 0) {
(void) snprintf(debug, sizeof (debug),
"SES%x iscsi session(%u) failed authentication, "
"received incorrect username from initiator",
isp->s_num);
queue_str(isp->s_mgmtq, Q_SESS_ERRS, msg_log, debug);
return (iscsiAuthStatusFail);
}
/* Check if RADIUS access is enabled */
if (persistent_radius_get(&p_radius_cfg) == B_TRUE &&
p_radius_cfg.r_radius_access == B_TRUE) {
chap_validation_status_type chap_valid_status;
int authStatus;
RADIUS_CONFIG radius_cfg;
if (p_radius_cfg.r_radius_config_valid == B_FALSE) {
/*
* Radius enabled but configuration invalid -
* invalid condition
*/
return (iscsiAuthStatusFail);
}
/* Use RADIUS server to authentication target */
if (p_radius_cfg.r_insize == sizeof (in_addr_t)) {
/* IPv4 */
radius_cfg.rad_svr_addr.i_addr.in4.s_addr =
p_radius_cfg.r_addr.u_in4.s_addr;
radius_cfg.rad_svr_addr.i_insize
= sizeof (in_addr_t);
} else if (p_radius_cfg.r_insize == sizeof (in6_addr_t)) {
/* IPv6 */
bcopy(p_radius_cfg.r_addr.u_in6.s6_addr,
radius_cfg.rad_svr_addr.i_addr.in6.s6_addr,
16);
radius_cfg.rad_svr_addr.i_insize = sizeof (in6_addr_t);
} else {
return (iscsiAuthStatusFail);
}
radius_cfg.rad_svr_port = p_radius_cfg.r_port;
bcopy(p_radius_cfg.r_shared_secret,
radius_cfg.rad_svr_shared_secret,
MAX_RAD_SHARED_SECRET_LEN);
radius_cfg.rad_svr_shared_secret_len =
p_radius_cfg.r_shared_secret_len;
chap_valid_status = radius_chap_validate(
isp->sess_auth.username_in,
isp->sess_auth.username,
challengeData,
challengeLength,
responseData,
responseLength,
id,
radius_cfg.rad_svr_addr,
radius_cfg.rad_svr_port,
radius_cfg.rad_svr_shared_secret,
radius_cfg.rad_svr_shared_secret_len);
switch (chap_valid_status) {
case CHAP_VALIDATION_PASSED:
authStatus = iscsiAuthStatusPass;
break;
case CHAP_VALIDATION_INVALID_RESPONSE:
authStatus = iscsiAuthStatusFail;
break;
case CHAP_VALIDATION_DUP_SECRET:
authStatus = iscsiAuthStatusFail;
break;
case CHAP_VALIDATION_RADIUS_ACCESS_ERROR:
authStatus = iscsiAuthStatusFail;
break;
case CHAP_VALIDATION_BAD_RADIUS_SECRET:
authStatus = iscsiAuthStatusFail;
break;
default:
authStatus = iscsiAuthStatusFail;
break;
}
return (authStatus);
} else {
/* Use target secret (if defined) to authenticate target */
if ((isp->sess_auth.password_length_in < 1) ||
(isp->sess_auth.password_in == NULL) ||
(isp->sess_auth.password_in[0] == '\0')) {
/* No target secret defined - invalid condition */
return (iscsiAuthStatusFail);
}
/*
* challenge length is I->T, and shouldn't need to
* be checked
*/
if (responseLength != sizeof (verifyData)) {
(void) snprintf(debug, sizeof (debug),
"SES%x iscsi session(%u) failed "
"authentication, received incorrect CHAP response "
"from initiator", isp->s_num);
queue_str(isp->s_mgmtq, Q_SESS_ERRS, msg_log, debug);
return (iscsiAuthStatusFail);
}
iscsiAuthMd5Init(&context);
/*
* id byte
*/
verifyData[0] = id;
iscsiAuthMd5Update(&context, verifyData, 1);
/*
* shared secret
*/
iscsiAuthMd5Update(&context,
(uchar_t *)isp->sess_auth.password_in,
isp->sess_auth.password_length_in);
/*
* challenge value
*/
iscsiAuthMd5Update(&context,
(uchar_t *)challengeData,
challengeLength);
iscsiAuthMd5Final(verifyData, &context);
if (bcmp(responseData, verifyData,
sizeof (verifyData)) == 0) {
return (iscsiAuthStatusPass);
}
(void) snprintf(debug, sizeof (debug),
"SES%x iscsi session(%u) failed authentication, "
"received incorrect CHAP response from initiator",
isp->s_num);
queue_str(isp->s_mgmtq, Q_SESS_ERRS, msg_log, debug);
}
return (iscsiAuthStatusFail);
}
int
iscsiAuthClientTextToNumber(const char *text, unsigned long *pNumber)
{
char *pEnd;
unsigned long number;
number = strtoul(text, &pEnd, 0);
if (*text != '\0' && *pEnd == '\0') {
*pNumber = number;
return (0); /* No error */
} else {
return (1); /* Error */
}
}
/* ARGSUSED */
void
iscsiAuthClientNumberToText(unsigned long number, char *text,
unsigned int length)
{
(void) snprintf(text, length, "%lu", number);
}
void
iscsiAuthRandomSetData(uchar_t *data, unsigned int length)
{
int fd;
fd = open("/dev/random", O_RDONLY);
if (fd == -1)
return;
(void) read(fd, data, length);
(void) close(fd);
}
void
iscsiAuthMd5Init(IscsiAuthMd5Context * context)
{
MD5Init(context);
}
void
iscsiAuthMd5Update(IscsiAuthMd5Context *context, uchar_t *data,
unsigned int length)
{
MD5Update(context, data, length);
}
void
iscsiAuthMd5Final(uchar_t *hash, IscsiAuthMd5Context *context)
{
MD5Final(hash, context);
}
int
iscsiAuthClientData(uchar_t *outData, unsigned int *outLength,
uchar_t *inData, unsigned int inLength)
{
if (*outLength < inLength) {
return (1); /* error */
}
bcopy(inData, outData, inLength);
*outLength = inLength;
return (0); /* no error */
}