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 (strcmp(username, isp->sess_auth.username_in) != 0) { 62 cmn_err(CE_WARN, "iscsi session(%u) failed authentication, " 63 "received incorrect username from target", 64 isp->sess_oid); 65 return (iscsiAuthStatusFail); 66 } 67 68 /* Check if RADIUS access is enabled */ 69 if (persistent_radius_get(&p_radius_cfg) == ISCSI_NVFILE_SUCCESS && 70 p_radius_cfg.r_radius_access == B_TRUE) { 71 chap_validation_status_type chap_valid_status; 72 int authStatus; 73 RADIUS_CONFIG radius_cfg; 74 75 if (p_radius_cfg.r_radius_config_valid == B_FALSE) { 76 /* 77 * Radius enabled but configuration invalid - 78 * invalid condition 79 */ 80 return (iscsiAuthStatusFail); 81 } 82 83 /* Use RADIUS server to authentication target */ 84 if (p_radius_cfg.r_insize == sizeof (in_addr_t)) { 85 /* IPv4 */ 86 radius_cfg.rad_svr_addr.i_addr.in4.s_addr = 87 p_radius_cfg.r_addr.u_in4.s_addr; 88 radius_cfg.rad_svr_addr.i_insize 89 = sizeof (in_addr_t); 90 } else if (p_radius_cfg.r_insize == sizeof (in6_addr_t)) { 91 /* IPv6 */ 92 bcopy(p_radius_cfg.r_addr.u_in6.s6_addr, 93 radius_cfg.rad_svr_addr.i_addr.in6.s6_addr, 94 16); 95 radius_cfg.rad_svr_addr.i_insize = sizeof (in6_addr_t); 96 } else { 97 return (iscsiAuthStatusFail); 98 } 99 100 radius_cfg.rad_svr_port = p_radius_cfg.r_port; 101 bcopy(p_radius_cfg.r_shared_secret, 102 radius_cfg.rad_svr_shared_secret, 103 MAX_RAD_SHARED_SECRET_LEN); 104 radius_cfg.rad_svr_shared_secret_len = 105 p_radius_cfg.r_shared_secret_len; 106 107 /* Entry point to the CHAP authentication module. */ 108 chap_valid_status = chap_validate_tgt( 109 isp->sess_auth.username_in, 110 isp->sess_auth.username, 111 challengeData, 112 challengeLength, 113 responseData, 114 responseLength, 115 id, 116 RADIUS_AUTHENTICATION, 117 (void *)&radius_cfg); 118 119 switch (chap_valid_status) { 120 case CHAP_VALIDATION_PASSED: 121 authStatus = iscsiAuthStatusPass; 122 break; 123 case CHAP_VALIDATION_INVALID_RESPONSE: 124 authStatus = iscsiAuthStatusFail; 125 break; 126 case CHAP_VALIDATION_DUP_SECRET: 127 authStatus = iscsiAuthStatusFail; 128 break; 129 case CHAP_VALIDATION_RADIUS_ACCESS_ERROR: 130 authStatus = iscsiAuthStatusFail; 131 break; 132 case CHAP_VALIDATION_BAD_RADIUS_SECRET: 133 authStatus = iscsiAuthStatusFail; 134 break; 135 default: 136 authStatus = iscsiAuthStatusFail; 137 break; 138 } 139 return (authStatus); 140 } else { 141 /* Use target secret (if defined) to authenticate target */ 142 if ((isp->sess_auth.password_length_in < 1) || 143 (isp->sess_auth.password_in[0] == '\0')) { 144 /* No target secret defined - invalid condition */ 145 return (iscsiAuthStatusFail); 146 } 147 148 /* 149 * challenge length is I->T, and shouldn't need to 150 * be checked 151 */ 152 if (responseLength != sizeof (verifyData)) { 153 cmn_err(CE_WARN, "iscsi session(%u) failed " 154 "authentication, received incorrect CHAP response " 155 "from target", isp->sess_oid); 156 return (iscsiAuthStatusFail); 157 } 158 159 iscsiAuthMd5Init(&context); 160 161 /* 162 * id byte 163 */ 164 verifyData[0] = id; 165 iscsiAuthMd5Update(&context, verifyData, 1); 166 167 /* 168 * shared secret 169 */ 170 iscsiAuthMd5Update(&context, 171 (uchar_t *)isp->sess_auth.password_in, 172 isp->sess_auth.password_length_in); 173 174 /* 175 * challenge value 176 */ 177 iscsiAuthMd5Update(&context, 178 (uchar_t *)challengeData, 179 challengeLength); 180 181 iscsiAuthMd5Final(verifyData, &context); 182 183 if (bcmp(responseData, verifyData, 184 sizeof (verifyData)) == 0) { 185 return (iscsiAuthStatusPass); 186 } 187 188 cmn_err(CE_WARN, "iscsi session(%u) failed authentication, " 189 "received incorrect CHAP response from target", 190 isp->sess_oid); 191 } 192 193 return (iscsiAuthStatusFail); 194 } 195 196 /* ARGSUSED */ 197 void 198 iscsiAuthClientChapAuthCancel(IscsiAuthClient * client) 199 { 200 } 201 202 203 int 204 iscsiAuthClientTextToNumber(const char *text, unsigned long *pNumber) 205 { 206 char *pEnd; 207 unsigned long number; 208 209 if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) { 210 if (ddi_strtoul(text + 2, &pEnd, 16, &number) != 0) { 211 return (1); /* Error */ 212 } 213 } else { 214 if (ddi_strtoul(text, &pEnd, 10, &number) != 0) { 215 return (1); /* Error */ 216 } 217 } 218 219 if (*text != '\0' && *pEnd == '\0') { 220 *pNumber = number; 221 return (0); /* No error */ 222 } else { 223 return (1); /* Error */ 224 } 225 } 226 227 /* ARGSUSED */ 228 void 229 iscsiAuthClientNumberToText(unsigned long number, char *text, 230 unsigned int length) 231 { 232 (void) sprintf(text, "%lu", number); 233 } 234 235 236 void 237 iscsiAuthRandomSetData(uchar_t *data, unsigned int length) 238 { 239 (void) random_get_pseudo_bytes(data, length); 240 } 241 242 243 void 244 iscsiAuthMd5Init(IscsiAuthMd5Context * context) 245 { 246 MD5Init(context); 247 } 248 249 250 void 251 iscsiAuthMd5Update(IscsiAuthMd5Context *context, uchar_t *data, 252 unsigned int length) 253 { 254 MD5Update(context, data, length); 255 } 256 257 258 void 259 iscsiAuthMd5Final(uchar_t *hash, IscsiAuthMd5Context *context) 260 { 261 MD5Final(hash, context); 262 } 263 264 265 int 266 iscsiAuthClientData(uchar_t *outData, unsigned int *outLength, 267 uchar_t *inData, unsigned int inLength) 268 { 269 if (*outLength < inLength) { 270 return (1); /* error */ 271 } 272 bcopy(inData, outData, inLength); 273 *outLength = inLength; 274 return (0); /* no error */ 275 } 276