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