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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/ddi.h> 27 #include <sys/sunddi.h> 28 #include <sys/iscsit/iscsi_if.h> 29 #include <sys/md5.h> 30 31 #include <sys/idm/idm.h> 32 #include <sys/idm/idm_so.h> 33 #include <sys/iscsit/radius_packet.h> 34 #include <sys/iscsit/radius_protocol.h> 35 #include <sys/ksocket.h> 36 37 static void encode_chap_password(int identifier, int chap_passwd_len, 38 uint8_t *chap_passwd, uint8_t *result); 39 40 static size_t iscsit_net_recvmsg(ksocket_t socket, struct msghdr *msg, 41 int timeout); 42 43 /* 44 * See radius_packet.h. 45 */ 46 int 47 iscsit_snd_radius_request(ksocket_t socket, iscsi_ipaddr_t rsvr_ip_addr, 48 uint32_t rsvr_port, radius_packet_data_t *req_data) 49 { 50 int i; /* Loop counter. */ 51 int data_len; 52 int len; 53 ushort_t total_length; /* Has to be 2 octets in size */ 54 uint8_t *ptr; /* Pointer to RADIUS packet data */ 55 uint8_t *length_ptr; /* Points to the Length field of the */ 56 /* packet. */ 57 uint8_t *data; /* RADIUS data to be sent */ 58 radius_attr_t *req_attr; /* Request attributes */ 59 radius_packet_t *packet; /* Outbound RADIUS packet */ 60 union { 61 struct sockaddr_in s_in4; 62 struct sockaddr_in6 s_in6; 63 } sa_rsvr; /* Socket address of the server */ 64 int err; 65 66 /* 67 * Create a RADIUS packet with minimal length for now. 68 */ 69 total_length = MIN_RAD_PACKET_LEN; 70 data = kmem_zalloc(MAX_RAD_PACKET_LEN, KM_SLEEP); 71 packet = (radius_packet_t *)data; 72 packet->code = req_data->code; 73 packet->identifier = req_data->identifier; 74 bcopy(req_data->authenticator, packet->authenticator, 75 RAD_AUTHENTICATOR_LEN); 76 ptr = packet->data; 77 78 /* Loop over all attributes of the request. */ 79 for (i = 0; i < req_data->num_of_attrs; i++) { 80 if (total_length > MAX_RAD_PACKET_LEN) { 81 /* The packet has exceed its maximum size. */ 82 kmem_free(data, MAX_RAD_PACKET_LEN); 83 return (-1); 84 } 85 86 req_attr = &req_data->attrs[i]; 87 *ptr++ = (req_attr->attr_type_code & 0xFF); 88 length_ptr = ptr; 89 /* Length is 2 octets - RFC 2865 section 3 */ 90 *ptr++ = 2; 91 total_length += 2; 92 93 /* If the attribute is CHAP-Password, encode it. */ 94 if (req_attr->attr_type_code == RAD_CHAP_PASSWORD) { 95 /* 96 * Identifier plus CHAP response. RFC 2865 97 * section 5.3. 98 */ 99 uint8_t encoded_chap_passwd[ 100 RAD_CHAP_PASSWD_STR_LEN + RAD_IDENTIFIER_LEN + 1]; 101 encode_chap_password( 102 req_data->identifier, 103 req_attr->attr_value_len, 104 req_attr->attr_value, 105 encoded_chap_passwd); 106 107 req_attr->attr_value_len = 108 RAD_CHAP_PASSWD_STR_LEN + RAD_IDENTIFIER_LEN; 109 110 bcopy(encoded_chap_passwd, 111 req_attr->attr_value, 112 req_attr->attr_value_len); 113 } 114 115 len = req_attr->attr_value_len; 116 *length_ptr += len; 117 118 bcopy(req_attr->attr_value, ptr, req_attr->attr_value_len); 119 ptr += req_attr->attr_value_len; 120 121 total_length += len; 122 } /* Done looping over all attributes */ 123 124 data_len = total_length; 125 total_length = htons(total_length); 126 bcopy(&total_length, packet->length, sizeof (ushort_t)); 127 128 /* 129 * Send the packet to the RADIUS server. 130 */ 131 bzero((char *)&sa_rsvr, sizeof (sa_rsvr)); 132 if (rsvr_ip_addr.i_insize == sizeof (in_addr_t)) { 133 134 /* IPv4 */ 135 sa_rsvr.s_in4.sin_family = AF_INET; 136 sa_rsvr.s_in4.sin_addr.s_addr = 137 rsvr_ip_addr.i_addr.in4.s_addr; 138 sa_rsvr.s_in4.sin_port = htons((ushort_t)rsvr_port); 139 140 err = idm_sosendto(socket, data, data_len, 141 (struct sockaddr *)&sa_rsvr.s_in4, 142 sizeof (struct sockaddr_in)); 143 kmem_free(data, MAX_RAD_PACKET_LEN); 144 return (err); 145 } else if (rsvr_ip_addr.i_insize == sizeof (in6_addr_t)) { 146 /* IPv6 */ 147 sa_rsvr.s_in6.sin6_family = AF_INET6; 148 bcopy(rsvr_ip_addr.i_addr.in6.s6_addr, 149 sa_rsvr.s_in6.sin6_addr.s6_addr, sizeof (struct in6_addr)); 150 sa_rsvr.s_in6.sin6_port = htons((ushort_t)rsvr_port); 151 152 err = idm_sosendto(socket, data, data_len, 153 (struct sockaddr *)&sa_rsvr.s_in6, 154 sizeof (struct sockaddr_in6)); 155 kmem_free(data, MAX_RAD_PACKET_LEN); 156 return (err); 157 } else { 158 /* Invalid IP address for RADIUS server. */ 159 kmem_free(data, MAX_RAD_PACKET_LEN); 160 return (-1); 161 } 162 } 163 164 /* 165 * See radius_packet.h. 166 */ 167 int 168 iscsit_rcv_radius_response(ksocket_t socket, uint8_t *shared_secret, 169 uint32_t shared_secret_len, uint8_t *req_authenticator, 170 radius_packet_data_t *resp_data) 171 { 172 radius_packet_t *packet; 173 MD5_CTX context; 174 uint8_t *tmp_data; 175 uint8_t md5_digest[16]; /* MD5 Digest Length 16 */ 176 uint16_t declared_len = 0; 177 size_t received_len = 0; 178 179 struct iovec iov[1]; 180 struct nmsghdr msg; 181 182 tmp_data = kmem_zalloc(MAX_RAD_PACKET_LEN, KM_SLEEP); 183 iov[0].iov_base = (char *)tmp_data; 184 iov[0].iov_len = MAX_RAD_PACKET_LEN; 185 186 bzero(&msg, sizeof (msg)); 187 msg.msg_name = NULL; 188 msg.msg_namelen = 0; 189 msg.msg_control = NULL; 190 msg.msg_controllen = 0; 191 msg.msg_flags = MSG_WAITALL; 192 msg.msg_iov = iov; 193 msg.msg_iovlen = 1; 194 195 received_len = iscsit_net_recvmsg(socket, &msg, RAD_RCV_TIMEOUT); 196 197 if (received_len <= (size_t)0) { 198 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 199 return (RAD_RSP_RCVD_NO_DATA); 200 } 201 202 /* 203 * Check if the received packet length is within allowable range. 204 * RFC 2865 section 3. 205 */ 206 if (received_len < MIN_RAD_PACKET_LEN) { 207 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 208 return (RAD_RSP_RCVD_PROTOCOL_ERR); 209 } else if (received_len > MAX_RAD_PACKET_LEN) { 210 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 211 return (RAD_RSP_RCVD_PROTOCOL_ERR); 212 } 213 214 packet = (radius_packet_t *)tmp_data; 215 bcopy(packet->length, &declared_len, sizeof (ushort_t)); 216 declared_len = ntohs(declared_len); 217 218 /* 219 * Discard packet with received length shorter than declared 220 * length. RFC 2865 section 3. 221 */ 222 if (received_len < declared_len) { 223 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 224 return (RAD_RSP_RCVD_PROTOCOL_ERR); 225 } 226 227 /* 228 * Check if the declared packet length is within allowable range. 229 * RFC 2865 section 3. 230 */ 231 if (declared_len < MIN_RAD_PACKET_LEN) { 232 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 233 return (RAD_RSP_RCVD_PROTOCOL_ERR); 234 } else if (declared_len > MAX_RAD_PACKET_LEN) { 235 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 236 return (RAD_RSP_RCVD_PROTOCOL_ERR); 237 } 238 239 /* 240 * Authenticate the incoming packet, using the following algorithm 241 * (RFC 2865 section 3): 242 * 243 * MD5(Code+ID+Length+RequestAuth+Attributes+Secret) 244 * 245 * Code = RADIUS packet code 246 * ID = RADIUS packet identifier 247 * Length = Declared length of the packet 248 * RequestAuth = The request authenticator 249 * Attributes = The response attributes 250 * Secret = The shared secret 251 */ 252 MD5Init(&context); 253 bzero(&md5_digest, 16); 254 MD5Update(&context, &packet->code, 1); 255 MD5Update(&context, &packet->identifier, 1); 256 MD5Update(&context, packet->length, 2); 257 MD5Update(&context, req_authenticator, RAD_AUTHENTICATOR_LEN); 258 259 /* 260 * Include response attributes only if there is a payload 261 * If the received length is greater than the declared length, 262 * trust the declared length and shorten the packet (i.e., to 263 * treat the octets outside the range of the Length field as 264 * padding - RFC 2865 section 3). 265 */ 266 if (declared_len > RAD_PACKET_HDR_LEN) { 267 /* Response Attributes */ 268 MD5Update(&context, packet->data, 269 declared_len - RAD_PACKET_HDR_LEN); 270 } 271 MD5Update(&context, shared_secret, shared_secret_len); 272 MD5Final(md5_digest, &context); 273 274 if (bcmp(md5_digest, packet->authenticator, RAD_AUTHENTICATOR_LEN) 275 != 0) { 276 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 277 return (RAD_RSP_RCVD_AUTH_FAILED); 278 } 279 280 /* 281 * Annotate the RADIUS packet data with the data we received from 282 * the server. 283 */ 284 resp_data->code = packet->code; 285 resp_data->identifier = packet->identifier; 286 287 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 288 return (RAD_RSP_RCVD_SUCCESS); 289 } 290 291 /* 292 * encode_chap_password - 293 * 294 * Encode a CHAP-Password attribute. This function basically prepends 295 * the identifier in front of chap_passwd and copy the results to 296 * *result. 297 */ 298 static void 299 encode_chap_password(int identifier, int chap_passwd_len, 300 uint8_t *chap_passwd, uint8_t *result) 301 { 302 result[0] = (uint8_t)identifier; 303 bcopy(chap_passwd, &result[1], chap_passwd_len); 304 } 305 /* 306 * iscsi_net_recvmsg - receive message on socket 307 */ 308 /* ARGSUSED */ 309 static size_t 310 iscsit_net_recvmsg(ksocket_t socket, struct msghdr *msg, int timeout) 311 { 312 int prflag = msg->msg_flags; 313 size_t recv = 0; 314 struct sockaddr_in6 l_addr, f_addr; 315 socklen_t l_addrlen; 316 socklen_t f_addrlen; 317 318 bzero(&l_addr, sizeof (struct sockaddr_in6)); 319 bzero(&f_addr, sizeof (struct sockaddr_in6)); 320 l_addrlen = sizeof (struct sockaddr_in6); 321 f_addrlen = sizeof (struct sockaddr_in6); 322 /* If timeout requested on receive */ 323 if (timeout > 0) { 324 boolean_t loopback = B_FALSE; 325 (void) ksocket_getsockname(socket, (struct sockaddr *)(&l_addr), 326 &l_addrlen, CRED()); 327 (void) ksocket_getpeername(socket, (struct sockaddr *)(&f_addr), 328 &f_addrlen, CRED()); 329 330 /* And this isn't a loopback connection */ 331 if (((struct sockaddr *)(&l_addr))->sa_family == AF_INET) { 332 struct sockaddr_in *lin = (struct sockaddr_in *) 333 ((void *)(&l_addr)); 334 struct sockaddr_in *fin = (struct sockaddr_in *) 335 ((void *)(&f_addr)); 336 337 if ((lin->sin_family == fin->sin_family) && 338 (bcmp(&lin->sin_addr, &fin->sin_addr, 339 sizeof (struct in_addr)) == 0)) { 340 loopback = B_TRUE; 341 } 342 } else { 343 struct sockaddr_in6 *lin6 = (struct sockaddr_in6 *) 344 ((void *)(&l_addr)); 345 struct sockaddr_in6 *fin6 = (struct sockaddr_in6 *) 346 ((void *)(&f_addr)); 347 348 if ((lin6->sin6_family == fin6->sin6_family) && 349 (bcmp(&lin6->sin6_addr, &fin6->sin6_addr, 350 sizeof (struct in6_addr)) == 0)) { 351 loopback = B_TRUE; 352 } 353 } 354 if (loopback == B_FALSE) { 355 struct timeval tl; 356 tl.tv_sec = timeout; 357 tl.tv_usec = 0; 358 /* Set recv timeout */ 359 if (ksocket_setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, 360 &tl, sizeof (struct timeval), CRED())) 361 return (0); 362 } 363 } 364 365 /* 366 * Receive the requested data. Block until all 367 * data is received or timeout. 368 * 369 * resid occurs only when the connection is 370 * disconnected. In that case it will return 371 * the amount of data that was not received. 372 * In general this is the total amount we 373 * requested. 374 */ 375 (void) ksocket_recvmsg(socket, msg, prflag, &recv, CRED()); 376 return (recv); 377 } 378