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(isp->sess_auth.username_in, 115 isp->sess_auth.username, challengeData, responseData, 116 id, RADIUS_AUTHENTICATION, (void *)&radius_cfg); 117 118 switch (chap_valid_status) { 119 case CHAP_VALIDATION_PASSED: 120 authStatus = iscsiAuthStatusPass; 121 break; 122 case CHAP_VALIDATION_INVALID_RESPONSE: 123 authStatus = iscsiAuthStatusFail; 124 break; 125 case CHAP_VALIDATION_DUP_SECRET: 126 authStatus = iscsiAuthStatusFail; 127 break; 128 case CHAP_VALIDATION_RADIUS_ACCESS_ERROR: 129 authStatus = iscsiAuthStatusFail; 130 break; 131 case CHAP_VALIDATION_BAD_RADIUS_SECRET: 132 authStatus = iscsiAuthStatusFail; 133 break; 134 default: 135 authStatus = iscsiAuthStatusFail; 136 break; 137 } 138 return (authStatus); 139 } else { 140 /* Use target secret (if defined) to authenticate target */ 141 if ((isp->sess_auth.password_length_in < 1) || 142 (isp->sess_auth.password_in == NULL) || 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