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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include "iscsi.h" 27 #include <sys/scsi/adapters/iscsi_if.h> 28 #include "radius_packet.h" 29 #include "radius_protocol.h" 30 #include <sys/int_types.h> 31 #include <sys/socket.h> 32 #include <sys/types.h> 33 #include <sys/sunddi.h> 34 35 static void encode_chap_password(int identifier, int chap_passwd_len, 36 uint8_t *chap_passwd, uint8_t *result); 37 38 /* 39 * See radius_packet.h. 40 */ 41 int 42 snd_radius_request(void *socket, iscsi_ipaddr_t rsvr_ip_addr, 43 uint32_t rsvr_port, radius_packet_data_t *req_data) 44 { 45 int i; /* Loop counter. */ 46 int data_len; 47 int len; 48 ushort_t total_length; /* Has to be 2 octets in size */ 49 uint8_t *ptr; /* Pointer to RADIUS packet data */ 50 uint8_t *length_ptr; /* Points to the Length field of the */ 51 /* packet. */ 52 uint8_t *data; /* RADIUS data to be sent */ 53 radius_attr_t *req_attr; /* Request attributes */ 54 radius_packet_t *packet; /* Outbound RADIUS packet */ 55 union { 56 struct sockaddr_in s_in4; 57 struct sockaddr_in6 s_in6; 58 } sa_rsvr; /* Socket address of the server */ 59 struct nmsghdr msg; 60 struct iovec iov[1]; 61 62 /* 63 * Create a RADIUS packet with minimal length for now. 64 */ 65 total_length = MIN_RAD_PACKET_LEN; 66 data = kmem_zalloc(MAX_RAD_PACKET_LEN, KM_SLEEP); 67 packet = (radius_packet_t *)data; 68 packet->code = req_data->code; 69 packet->identifier = req_data->identifier; 70 bcopy(req_data->authenticator, packet->authenticator, 71 RAD_AUTHENTICATOR_LEN); 72 ptr = packet->data; 73 74 /* Loop over all attributes of the request. */ 75 for (i = 0; i < req_data->num_of_attrs; i++) { 76 if (total_length > MAX_RAD_PACKET_LEN) { 77 /* The packet has exceed its maximum size. */ 78 kmem_free(data, MAX_RAD_PACKET_LEN); 79 return (-1); 80 } 81 82 req_attr = &req_data->attrs[i]; 83 *ptr++ = (req_attr->attr_type_code & 0xFF); 84 length_ptr = ptr; 85 /* Length is 2 octets - RFC 2865 section 3 */ 86 *ptr++ = 2; 87 total_length += 2; 88 89 /* If the attribute is CHAP-Password, encode it. */ 90 if (req_attr->attr_type_code == RAD_CHAP_PASSWORD) { 91 /* 92 * Identifier plus CHAP response. RFC 2865 93 * section 5.3. 94 */ 95 uint8_t encoded_chap_passwd[RAD_CHAP_PASSWD_STR_LEN + 96 RAD_IDENTIFIER_LEN + 1]; 97 encode_chap_password 98 (req_data->identifier, 99 req_attr->attr_value_len, 100 req_attr->attr_value, 101 encoded_chap_passwd); 102 103 req_attr->attr_value_len = RAD_CHAP_PASSWD_STR_LEN + 104 RAD_IDENTIFIER_LEN; 105 106 bcopy(encoded_chap_passwd, 107 req_attr->attr_value, 108 req_attr->attr_value_len); 109 } 110 111 len = req_attr->attr_value_len; 112 *length_ptr += len; 113 114 bcopy(req_attr->attr_value, ptr, req_attr->attr_value_len); 115 ptr += req_attr->attr_value_len; 116 117 total_length += len; 118 } /* Done looping over all attributes */ 119 120 data_len = total_length; 121 total_length = htons(total_length); 122 bcopy(&total_length, packet->length, sizeof (ushort_t)); 123 124 /* 125 * Send the packet to the RADIUS server. 126 */ 127 bzero((char *)&sa_rsvr, sizeof (sa_rsvr)); 128 if (rsvr_ip_addr.i_insize == sizeof (in_addr_t)) { 129 int recv_len; 130 131 /* IPv4 */ 132 sa_rsvr.s_in4.sin_family = AF_INET; 133 sa_rsvr.s_in4.sin_addr.s_addr = 134 rsvr_ip_addr.i_addr.in4.s_addr; 135 sa_rsvr.s_in4.sin_port = htons((ushort_t)rsvr_port); 136 137 iov[0].iov_base = (char *)data; 138 iov[0].iov_len = data_len; 139 140 bzero(&msg, sizeof (msg)); 141 msg.msg_name = (struct sockaddr *)&sa_rsvr.s_in4; 142 msg.msg_namelen = sizeof (struct sockaddr_in); 143 msg.msg_iov = iov; 144 msg.msg_iovlen = 1; 145 146 recv_len = iscsi_net->sendmsg(socket, &msg); 147 kmem_free(data, MAX_RAD_PACKET_LEN); 148 return (recv_len == data_len ? 0 : -1); 149 } else if (rsvr_ip_addr.i_insize == sizeof (in6_addr_t)) { 150 /* No IPv6 support for now. */ 151 return (-1); 152 } else { 153 /* Invalid IP address for RADIUS server. */ 154 kmem_free(data, MAX_RAD_PACKET_LEN); 155 return (-1); 156 } 157 } 158 159 /* 160 * See radius_packet.h. 161 */ 162 int 163 rcv_radius_response(void *socket, uint8_t *shared_secret, 164 uint32_t shared_secret_len, uint8_t *req_authenticator, 165 radius_packet_data_t *resp_data) 166 { 167 int rcv_len = 0; 168 radius_packet_t *packet; 169 MD5_CTX context; 170 uint8_t *tmp_data; 171 uint8_t md5_digest[16]; /* MD5 Digest Length 16 */ 172 uint16_t declared_len = 0; 173 ushort_t len; 174 struct nmsghdr msg; 175 struct iovec iov[1]; 176 177 tmp_data = kmem_zalloc(MAX_RAD_PACKET_LEN, KM_SLEEP); 178 179 iov[0].iov_base = (char *)tmp_data; 180 iov[0].iov_len = MAX_RAD_PACKET_LEN; 181 182 bzero(&msg, sizeof (msg)); 183 msg.msg_name = NULL; 184 msg.msg_namelen = 0; 185 msg.msg_control = NULL; 186 msg.msg_controllen = 0; 187 msg.msg_flags = MSG_WAITALL; 188 msg.msg_iov = iov; 189 msg.msg_iovlen = 1; 190 191 rcv_len = iscsi_net->recvmsg(socket, &msg, RAD_RCV_TIMEOUT); 192 if (rcv_len == 0) { 193 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 194 return (RAD_RSP_RCVD_NO_DATA); 195 } 196 197 DTRACE_PROBE1(rcv_rad_resp_summary, int, rcv_len); 198 199 packet = (radius_packet_t *)tmp_data; 200 bcopy(packet->length, &len, sizeof (ushort_t)); 201 declared_len = ntohs(len); 202 203 DTRACE_PROBE1(rcv_rad_resp_data, uint16_t, declared_len); 204 205 /* 206 * Check if the received packet length is within allowable range. 207 * RFC 2865 section 3. 208 */ 209 if (rcv_len < MIN_RAD_PACKET_LEN) { 210 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 211 return (RAD_RSP_RCVD_PROTOCOL_ERR); 212 } else if (rcv_len > MAX_RAD_PACKET_LEN) { 213 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 214 return (RAD_RSP_RCVD_PROTOCOL_ERR); 215 } 216 217 /* 218 * Check if the declared packet length is within allowable range. 219 * RFC 2865 section 3. 220 */ 221 if (declared_len < MIN_RAD_PACKET_LEN) { 222 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 223 return (RAD_RSP_RCVD_PROTOCOL_ERR); 224 } else if (declared_len > MAX_RAD_PACKET_LEN) { 225 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 226 return (RAD_RSP_RCVD_PROTOCOL_ERR); 227 } 228 229 /* 230 * Discard packet with received length shorter than declared 231 * length. RFC 2865 section 3. 232 */ 233 if (rcv_len < declared_len) { 234 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 235 return (RAD_RSP_RCVD_PROTOCOL_ERR); 236 } 237 238 /* 239 * Authenticate the incoming packet, using the following algorithm 240 * (RFC 2865 section 3): 241 * 242 * MD5(Code+ID+Length+RequestAuth+Attributes+Secret) 243 * 244 * Code = RADIUS packet code 245 * ID = RADIUS packet identifier 246 * Length = Declared length of the packet 247 * RequestAuth = The request authenticator 248 * Attributes = The response attributes 249 * Secret = The shared secret 250 */ 251 MD5Init(&context); 252 bzero(&md5_digest, 16); 253 MD5Update(&context, &packet->code, 1); 254 MD5Update(&context, &packet->identifier, 1); 255 MD5Update(&context, packet->length, 2); 256 MD5Update(&context, req_authenticator, RAD_AUTHENTICATOR_LEN); 257 /* Include response attributes only if there is a payload */ 258 if (declared_len > RAD_PACKET_HDR_LEN) { 259 /* Response Attributes */ 260 MD5Update(&context, packet->data, 261 declared_len - RAD_PACKET_HDR_LEN); 262 } 263 MD5Update(&context, shared_secret, shared_secret_len); 264 MD5Final(md5_digest, &context); 265 266 if (bcmp(md5_digest, packet->authenticator, RAD_AUTHENTICATOR_LEN) 267 != 0) { 268 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 269 return (RAD_RSP_RCVD_AUTH_FAILED); 270 } 271 272 /* 273 * If the received length is greater than the declared length, 274 * trust the declared length and shorten the packet (i.e., to 275 * treat the octets outside the range of the Length field as 276 * padding - RFC 2865 section 3). 277 */ 278 if (rcv_len > declared_len) { 279 /* Clear the padding data. */ 280 bzero(tmp_data + declared_len, rcv_len - declared_len); 281 rcv_len = declared_len; 282 } 283 284 /* 285 * Annotate the RADIUS packet data with the data we received from 286 * the server. 287 */ 288 resp_data->code = packet->code; 289 resp_data->identifier = packet->identifier; 290 291 kmem_free(tmp_data, MAX_RAD_PACKET_LEN); 292 return (RAD_RSP_RCVD_SUCCESS); 293 } 294 295 /* 296 * encode_chap_password - 297 * 298 * Encode a CHAP-Password attribute. This function basically prepends 299 * the identifier in front of chap_passwd and copy the results to 300 * *result. 301 */ 302 static void 303 encode_chap_password(int identifier, int chap_passwd_len, 304 uint8_t *chap_passwd, uint8_t *result) 305 { 306 int i; 307 uint8_t *p; 308 uint8_t tmp_result[RAD_CHAP_PASSWD_STR_LEN + 309 RAD_IDENTIFIER_LEN + 1]; 310 311 p = tmp_result; 312 *p = identifier; /* Identifier is 1 octet */ 313 p++; 314 for (i = 0; i < chap_passwd_len; i++) { 315 *p = chap_passwd[i]; 316 p++; 317 } 318 319 bcopy(tmp_result, result, 320 RAD_CHAP_PASSWD_STR_LEN + RAD_IDENTIFIER_LEN); 321 } 322