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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 #include <sys/sunddi.h>
28 #include <sys/iscsit/iscsi_if.h>
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>
37 static void encode_chap_password(int identifier
, int chap_passwd_len
,
38 uint8_t *chap_passwd
, uint8_t *result
);
40 static size_t iscsit_net_recvmsg(ksocket_t socket
, struct msghdr
*msg
,
44 * See radius_packet.h.
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
)
50 int i
; /* Loop counter. */
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 */
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 */
61 struct sockaddr_in s_in4
;
62 struct sockaddr_in6 s_in6
;
63 } sa_rsvr
; /* Socket address of the server */
67 * Create a RADIUS packet with minimal length for now.
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
);
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
);
86 req_attr
= &req_data
->attrs
[i
];
87 *ptr
++ = (req_attr
->attr_type_code
& 0xFF);
89 /* Length is 2 octets - RFC 2865 section 3 */
93 /* If the attribute is CHAP-Password, encode it. */
94 if (req_attr
->attr_type_code
== RAD_CHAP_PASSWORD
) {
96 * Identifier plus CHAP response. RFC 2865
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
);
107 req_attr
->attr_value_len
=
108 RAD_CHAP_PASSWD_STR_LEN
+ RAD_IDENTIFIER_LEN
;
110 bcopy(encoded_chap_passwd
,
111 req_attr
->attr_value
,
112 req_attr
->attr_value_len
);
115 len
= req_attr
->attr_value_len
;
118 bcopy(req_attr
->attr_value
, ptr
, req_attr
->attr_value_len
);
119 ptr
+= req_attr
->attr_value_len
;
122 } /* Done looping over all attributes */
124 data_len
= total_length
;
125 total_length
= htons(total_length
);
126 bcopy(&total_length
, packet
->length
, sizeof (ushort_t
));
129 * Send the packet to the RADIUS server.
131 bzero((char *)&sa_rsvr
, sizeof (sa_rsvr
));
132 if (rsvr_ip_addr
.i_insize
== sizeof (in_addr_t
)) {
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
);
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
);
145 } else if (rsvr_ip_addr
.i_insize
== sizeof (in6_addr_t
)) {
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
);
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
);
158 /* Invalid IP address for RADIUS server. */
159 kmem_free(data
, MAX_RAD_PACKET_LEN
);
165 * See radius_packet.h.
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
)
172 radius_packet_t
*packet
;
175 uint8_t md5_digest
[16]; /* MD5 Digest Length 16 */
176 uint16_t declared_len
= 0;
177 size_t received_len
= 0;
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
;
186 bzero(&msg
, sizeof (msg
));
189 msg
.msg_control
= NULL
;
190 msg
.msg_controllen
= 0;
191 msg
.msg_flags
= MSG_WAITALL
;
195 received_len
= iscsit_net_recvmsg(socket
, &msg
, RAD_RCV_TIMEOUT
);
197 if (received_len
<= (size_t)0) {
198 kmem_free(tmp_data
, MAX_RAD_PACKET_LEN
);
199 return (RAD_RSP_RCVD_NO_DATA
);
203 * Check if the received packet length is within allowable range.
204 * RFC 2865 section 3.
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
);
214 packet
= (radius_packet_t
*)tmp_data
;
215 bcopy(packet
->length
, &declared_len
, sizeof (ushort_t
));
216 declared_len
= ntohs(declared_len
);
219 * Discard packet with received length shorter than declared
220 * length. RFC 2865 section 3.
222 if (received_len
< declared_len
) {
223 kmem_free(tmp_data
, MAX_RAD_PACKET_LEN
);
224 return (RAD_RSP_RCVD_PROTOCOL_ERR
);
228 * Check if the declared packet length is within allowable range.
229 * RFC 2865 section 3.
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
);
240 * Authenticate the incoming packet, using the following algorithm
241 * (RFC 2865 section 3):
243 * MD5(Code+ID+Length+RequestAuth+Attributes+Secret)
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
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
);
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).
266 if (declared_len
> RAD_PACKET_HDR_LEN
) {
267 /* Response Attributes */
268 MD5Update(&context
, packet
->data
,
269 declared_len
- RAD_PACKET_HDR_LEN
);
271 MD5Update(&context
, shared_secret
, shared_secret_len
);
272 MD5Final(md5_digest
, &context
);
274 if (bcmp(md5_digest
, packet
->authenticator
, RAD_AUTHENTICATOR_LEN
)
276 kmem_free(tmp_data
, MAX_RAD_PACKET_LEN
);
277 return (RAD_RSP_RCVD_AUTH_FAILED
);
281 * Annotate the RADIUS packet data with the data we received from
284 resp_data
->code
= packet
->code
;
285 resp_data
->identifier
= packet
->identifier
;
287 kmem_free(tmp_data
, MAX_RAD_PACKET_LEN
);
288 return (RAD_RSP_RCVD_SUCCESS
);
292 * encode_chap_password -
294 * Encode a CHAP-Password attribute. This function basically prepends
295 * the identifier in front of chap_passwd and copy the results to
299 encode_chap_password(int identifier
, int chap_passwd_len
,
300 uint8_t *chap_passwd
, uint8_t *result
)
302 result
[0] = (uint8_t)identifier
;
303 bcopy(chap_passwd
, &result
[1], chap_passwd_len
);
306 * iscsi_net_recvmsg - receive message on socket
310 iscsit_net_recvmsg(ksocket_t socket
, struct msghdr
*msg
, int timeout
)
312 int prflag
= msg
->msg_flags
;
314 struct sockaddr_in6 l_addr
, f_addr
;
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 */
324 boolean_t loopback
= B_FALSE
;
325 (void) ksocket_getsockname(socket
, (struct sockaddr
*)(&l_addr
),
327 (void) ksocket_getpeername(socket
, (struct sockaddr
*)(&f_addr
),
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
*)
334 struct sockaddr_in
*fin
= (struct sockaddr_in
*)
337 if ((lin
->sin_family
== fin
->sin_family
) &&
338 (bcmp(&lin
->sin_addr
, &fin
->sin_addr
,
339 sizeof (struct in_addr
)) == 0)) {
343 struct sockaddr_in6
*lin6
= (struct sockaddr_in6
*)
345 struct sockaddr_in6
*fin6
= (struct sockaddr_in6
*)
348 if ((lin6
->sin6_family
== fin6
->sin6_family
) &&
349 (bcmp(&lin6
->sin6_addr
, &fin6
->sin6_addr
,
350 sizeof (struct in6_addr
)) == 0)) {
354 if (loopback
== B_FALSE
) {
358 /* Set recv timeout */
359 if (ksocket_setsockopt(socket
, SOL_SOCKET
, SO_RCVTIMEO
,
360 &tl
, sizeof (struct timeval
), CRED()))
366 * Receive the requested data. Block until all
367 * data is received or timeout.
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
375 (void) ksocket_recvmsg(socket
, msg
, prflag
, &recv
, CRED());