Home | History | Annotate | Download | only in iscsi
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2000 by Cisco Systems, Inc.  All rights reserved.
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  *
     26  * iSCSI Pseudo HBA Driver
     27  */
     28 
     29 #include <sys/random.h>
     30 
     31 #include "chap.h"
     32 #include "iscsi.h"
     33 #include <sys/iscsi_protocol.h>
     34 #include "iscsiAuthClient.h"
     35 #include "persistent.h"
     36 
     37 /*
     38  * Authenticate a target's CHAP response.
     39  *
     40  * username - Incoming username from the the target.
     41  * responseData - Incoming response data from the target.
     42  */
     43 int
     44 iscsiAuthClientChapAuthRequest(IscsiAuthClient *client,
     45     char *username, unsigned int id, uchar_t *challengeData,
     46     unsigned int challengeLength, uchar_t *responseData,
     47     unsigned int responseLength)
     48 {
     49 	iscsi_sess_t		*isp = (iscsi_sess_t *)client->userHandle;
     50 	IscsiAuthMd5Context	context;
     51 	uchar_t			verifyData[16];
     52 	iscsi_radius_props_t p_radius_cfg;
     53 
     54 	if (isp == NULL) {
     55 		return (iscsiAuthStatusFail);
     56 	}
     57 
     58 	/*
     59 	 * the expected credentials are in the session
     60 	 */
     61 	if (isp->sess_auth.username_in == NULL) {
     62 		cmn_err(CE_WARN, "iscsi session(%u) failed authentication, "
     63 		    "no incoming username configured to authenticate target",
     64 		    isp->sess_oid);
     65 		return (iscsiAuthStatusFail);
     66 	}
     67 	if (strcmp(username, isp->sess_auth.username_in) != 0) {
     68 		cmn_err(CE_WARN, "iscsi session(%u) failed authentication, "
     69 		    "received incorrect username from target",
     70 		    isp->sess_oid);
     71 		return (iscsiAuthStatusFail);
     72 	}
     73 
     74 	/* Check if RADIUS access is enabled */
     75 	if (persistent_radius_get(&p_radius_cfg) == ISCSI_NVFILE_SUCCESS &&
     76 	    p_radius_cfg.r_radius_access == B_TRUE) {
     77 		chap_validation_status_type chap_valid_status;
     78 		int authStatus;
     79 		RADIUS_CONFIG radius_cfg;
     80 
     81 		if (p_radius_cfg.r_radius_config_valid == B_FALSE) {
     82 			/*
     83 			 * Radius enabled but configuration invalid -
     84 			 * invalid condition
     85 			 */
     86 			return (iscsiAuthStatusFail);
     87 		}
     88 
     89 		/* Use RADIUS server to authentication target */
     90 		if (p_radius_cfg.r_insize == sizeof (in_addr_t)) {
     91 			/* IPv4 */
     92 			radius_cfg.rad_svr_addr.i_addr.in4.s_addr =
     93 			    p_radius_cfg.r_addr.u_in4.s_addr;
     94 			radius_cfg.rad_svr_addr.i_insize
     95 			    = sizeof (in_addr_t);
     96 		} else if (p_radius_cfg.r_insize == sizeof (in6_addr_t)) {
     97 			/* IPv6 */
     98 			bcopy(p_radius_cfg.r_addr.u_in6.s6_addr,
     99 			    radius_cfg.rad_svr_addr.i_addr.in6.s6_addr,
    100 			    16);
    101 			radius_cfg.rad_svr_addr.i_insize = sizeof (in6_addr_t);
    102 		} else {
    103 			return (iscsiAuthStatusFail);
    104 		}
    105 
    106 		radius_cfg.rad_svr_port = p_radius_cfg.r_port;
    107 		bcopy(p_radius_cfg.r_shared_secret,
    108 		    radius_cfg.rad_svr_shared_secret,
    109 		    MAX_RAD_SHARED_SECRET_LEN);
    110 		radius_cfg.rad_svr_shared_secret_len =
    111 		    p_radius_cfg.r_shared_secret_len;
    112 
    113 		/* Entry point to the CHAP authentication module. */
    114 		chap_valid_status = chap_validate_tgt(
    115 		    isp->sess_auth.username_in,
    116 		    isp->sess_auth.username,
    117 		    challengeData,
    118 		    challengeLength,
    119 		    responseData,
    120 		    responseLength,
    121 		    id,
    122 		    RADIUS_AUTHENTICATION,
    123 		    (void *)&radius_cfg);
    124 
    125 		switch (chap_valid_status) {
    126 			case CHAP_VALIDATION_PASSED:
    127 				authStatus = iscsiAuthStatusPass;
    128 				break;
    129 			case CHAP_VALIDATION_INVALID_RESPONSE:
    130 				authStatus = iscsiAuthStatusFail;
    131 				break;
    132 			case CHAP_VALIDATION_DUP_SECRET:
    133 				authStatus = iscsiAuthStatusFail;
    134 				break;
    135 			case CHAP_VALIDATION_RADIUS_ACCESS_ERROR:
    136 				authStatus = iscsiAuthStatusFail;
    137 				break;
    138 			case CHAP_VALIDATION_BAD_RADIUS_SECRET:
    139 				authStatus = iscsiAuthStatusFail;
    140 				break;
    141 			default:
    142 				authStatus = iscsiAuthStatusFail;
    143 				break;
    144 		}
    145 		return (authStatus);
    146 	} else {
    147 		/* Use target secret (if defined) to authenticate target */
    148 		if ((isp->sess_auth.password_length_in < 1) ||
    149 		    (isp->sess_auth.password_in == NULL) ||
    150 		    (isp->sess_auth.password_in[0] == '\0')) {
    151 			/* No target secret defined - invalid condition */
    152 			return (iscsiAuthStatusFail);
    153 		}
    154 
    155 		/*
    156 		 * challenge length is I->T, and shouldn't need to
    157 		 * be checked
    158 		 */
    159 		if (responseLength != sizeof (verifyData)) {
    160 			cmn_err(CE_WARN, "iscsi session(%u) failed "
    161 			    "authentication, received incorrect CHAP response "
    162 			    "from target", isp->sess_oid);
    163 			return (iscsiAuthStatusFail);
    164 		}
    165 
    166 		iscsiAuthMd5Init(&context);
    167 
    168 		/*
    169 		 * id byte
    170 		 */
    171 		verifyData[0] = id;
    172 		iscsiAuthMd5Update(&context, verifyData, 1);
    173 
    174 		/*
    175 		 * shared secret
    176 		 */
    177 		iscsiAuthMd5Update(&context,
    178 		    (uchar_t *)isp->sess_auth.password_in,
    179 		    isp->sess_auth.password_length_in);
    180 
    181 		/*
    182 		 * challenge value
    183 		 */
    184 		iscsiAuthMd5Update(&context,
    185 		    (uchar_t *)challengeData,
    186 		    challengeLength);
    187 
    188 		iscsiAuthMd5Final(verifyData, &context);
    189 
    190 		if (bcmp(responseData, verifyData,
    191 		    sizeof (verifyData)) == 0) {
    192 			return (iscsiAuthStatusPass);
    193 		}
    194 
    195 		cmn_err(CE_WARN, "iscsi session(%u) failed authentication, "
    196 		    "received incorrect CHAP response from target",
    197 		    isp->sess_oid);
    198 	}
    199 
    200 	return (iscsiAuthStatusFail);
    201 }
    202 
    203 /* ARGSUSED */
    204 void
    205 iscsiAuthClientChapAuthCancel(IscsiAuthClient * client)
    206 {
    207 }
    208 
    209 
    210 int
    211 iscsiAuthClientTextToNumber(const char *text, unsigned long *pNumber)
    212 {
    213 	char *pEnd;
    214 	unsigned long number;
    215 
    216 	if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) {
    217 		if (ddi_strtoul(text + 2, &pEnd, 16, &number) != 0) {
    218 			return (1); /* Error */
    219 		}
    220 	} else {
    221 		if (ddi_strtoul(text, &pEnd, 10, &number) != 0) {
    222 			return (1); /* Error */
    223 		}
    224 	}
    225 
    226 	if (*text != '\0' && *pEnd == '\0') {
    227 		*pNumber = number;
    228 		return (0);	/* No error */
    229 	} else {
    230 		return (1);	/* Error */
    231 	}
    232 }
    233 
    234 /* ARGSUSED */
    235 void
    236 iscsiAuthClientNumberToText(unsigned long number, char *text,
    237     unsigned int length)
    238 {
    239 	(void) sprintf(text, "%lu", number);
    240 }
    241 
    242 
    243 void
    244 iscsiAuthRandomSetData(uchar_t *data, unsigned int length)
    245 {
    246 	(void) random_get_pseudo_bytes(data, length);
    247 }
    248 
    249 
    250 void
    251 iscsiAuthMd5Init(IscsiAuthMd5Context * context)
    252 {
    253 	MD5Init(context);
    254 }
    255 
    256 
    257 void
    258 iscsiAuthMd5Update(IscsiAuthMd5Context *context, uchar_t *data,
    259     unsigned int length)
    260 {
    261 	MD5Update(context, data, length);
    262 }
    263 
    264 
    265 void
    266 iscsiAuthMd5Final(uchar_t *hash, IscsiAuthMd5Context *context)
    267 {
    268 	MD5Final(hash, context);
    269 }
    270 
    271 
    272 int
    273 iscsiAuthClientData(uchar_t *outData, unsigned int *outLength,
    274     uchar_t *inData, unsigned int inLength)
    275 {
    276 	if (*outLength < inLength) {
    277 		return (1);	/* error */
    278 	}
    279 	bcopy(inData, outData, inLength);
    280 	*outLength = inLength;
    281 	return (0);		/* no error */
    282 }
    283