1 /*- 2 * Copyright (c) 2012 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Edward Tomasz Napierala under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <assert.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <netinet/in.h> 39 #include <netdb.h> 40 #include <sys/socket.h> 41 42 #include "ctld.h" 43 #include "iscsi_proto.h" 44 45 static struct pdu * 46 text_receive(struct connection *conn) 47 { 48 struct pdu *request; 49 struct iscsi_bhs_text_request *bhstr; 50 51 request = pdu_new(conn); 52 pdu_receive(request); 53 if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 54 ISCSI_BHS_OPCODE_TEXT_REQUEST) 55 log_errx(1, "protocol error: received invalid opcode 0x%x", 56 request->pdu_bhs->bhs_opcode); 57 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 58 #if 0 59 if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0) 60 log_errx(1, "received Text PDU without the \"F\" flag"); 61 #endif 62 /* 63 * XXX: Implement the C flag some day. 64 */ 65 if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) 66 log_errx(1, "received Text PDU with unsupported \"C\" flag"); 67 if (ISCSI_SNLT(ntohl(bhstr->bhstr_cmdsn), conn->conn_cmdsn)) { 68 log_errx(1, "received Text PDU with decreasing CmdSN: " 69 "was %u, is %u", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); 70 } 71 if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { 72 log_errx(1, "received Text PDU with wrong ExpStatSN: " 73 "is %u, should be %u", ntohl(bhstr->bhstr_expstatsn), 74 conn->conn_statsn); 75 } 76 conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); 77 if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) 78 conn->conn_cmdsn++; 79 80 return (request); 81 } 82 83 static struct pdu * 84 text_new_response(struct pdu *request) 85 { 86 struct pdu *response; 87 struct connection *conn; 88 struct iscsi_bhs_text_request *bhstr; 89 struct iscsi_bhs_text_response *bhstr2; 90 91 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 92 conn = request->pdu_connection; 93 94 response = pdu_new_response(request); 95 bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; 96 bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; 97 bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; 98 bhstr2->bhstr_lun = bhstr->bhstr_lun; 99 bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; 100 bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; 101 bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); 102 bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); 103 bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); 104 105 return (response); 106 } 107 108 static struct pdu * 109 logout_receive(struct connection *conn) 110 { 111 struct pdu *request; 112 struct iscsi_bhs_logout_request *bhslr; 113 114 request = pdu_new(conn); 115 pdu_receive(request); 116 if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 117 ISCSI_BHS_OPCODE_LOGOUT_REQUEST) 118 log_errx(1, "protocol error: received invalid opcode 0x%x", 119 request->pdu_bhs->bhs_opcode); 120 bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 121 if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) 122 log_debugx("received Logout PDU with invalid reason 0x%x; " 123 "continuing anyway", bhslr->bhslr_reason & 0x7f); 124 if (ISCSI_SNLT(ntohl(bhslr->bhslr_cmdsn), conn->conn_cmdsn)) { 125 log_errx(1, "received Logout PDU with decreasing CmdSN: " 126 "was %u, is %u", conn->conn_cmdsn, 127 ntohl(bhslr->bhslr_cmdsn)); 128 } 129 if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 130 log_errx(1, "received Logout PDU with wrong ExpStatSN: " 131 "is %u, should be %u", ntohl(bhslr->bhslr_expstatsn), 132 conn->conn_statsn); 133 } 134 conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 135 if ((bhslr->bhslr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) 136 conn->conn_cmdsn++; 137 138 return (request); 139 } 140 141 static struct pdu * 142 logout_new_response(struct pdu *request) 143 { 144 struct pdu *response; 145 struct connection *conn; 146 struct iscsi_bhs_logout_request *bhslr; 147 struct iscsi_bhs_logout_response *bhslr2; 148 149 bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 150 conn = request->pdu_connection; 151 152 response = pdu_new_response(request); 153 bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; 154 bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; 155 bhslr2->bhslr_flags = 0x80; 156 bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; 157 bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 158 bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 159 bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 160 bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 161 162 return (response); 163 } 164 165 static void 166 discovery_add_target(struct keys *response_keys, const struct target *targ) 167 { 168 struct port *port; 169 struct portal *portal; 170 char *buf; 171 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 172 struct addrinfo *ai; 173 int ret; 174 175 keys_add(response_keys, "TargetName", targ->t_name); 176 TAILQ_FOREACH(port, &targ->t_ports, p_ts) { 177 if (port->p_portal_group == NULL) 178 continue; 179 TAILQ_FOREACH(portal, &port->p_portal_group->pg_portals, p_next) { 180 ai = portal->p_ai; 181 ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, 182 hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), 183 NI_NUMERICHOST | NI_NUMERICSERV); 184 if (ret != 0) { 185 log_warnx("getnameinfo: %s", gai_strerror(ret)); 186 continue; 187 } 188 switch (ai->ai_addr->sa_family) { 189 case AF_INET: 190 if (strcmp(hbuf, "0.0.0.0") == 0) 191 continue; 192 ret = asprintf(&buf, "%s:%s,%d", hbuf, sbuf, 193 port->p_portal_group->pg_tag); 194 break; 195 case AF_INET6: 196 if (strcmp(hbuf, "::") == 0) 197 continue; 198 ret = asprintf(&buf, "[%s]:%s,%d", hbuf, sbuf, 199 port->p_portal_group->pg_tag); 200 break; 201 default: 202 continue; 203 } 204 if (ret <= 0) 205 log_err(1, "asprintf"); 206 keys_add(response_keys, "TargetAddress", buf); 207 free(buf); 208 } 209 } 210 } 211 212 static bool 213 discovery_target_filtered_out(const struct connection *conn, 214 const struct port *port) 215 { 216 const struct auth_group *ag; 217 const struct portal_group *pg; 218 const struct target *targ; 219 const struct auth *auth; 220 int error; 221 222 targ = port->p_target; 223 ag = port->p_auth_group; 224 if (ag == NULL) 225 ag = targ->t_auth_group; 226 pg = conn->conn_portal->p_portal_group; 227 228 assert(pg->pg_discovery_auth_group != PG_FILTER_UNKNOWN); 229 230 if (pg->pg_discovery_filter >= PG_FILTER_PORTAL && 231 auth_portal_check(ag, &conn->conn_initiator_sa) != 0) { 232 log_debugx("initiator does not match initiator portals " 233 "allowed for target \"%s\"; skipping", targ->t_name); 234 return (true); 235 } 236 237 if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME && 238 auth_name_check(ag, conn->conn_initiator_name) != 0) { 239 log_debugx("initiator does not match initiator names " 240 "allowed for target \"%s\"; skipping", targ->t_name); 241 return (true); 242 } 243 244 if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME_AUTH && 245 ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { 246 if (conn->conn_chap == NULL) { 247 assert(pg->pg_discovery_auth_group->ag_type == 248 AG_TYPE_NO_AUTHENTICATION); 249 250 log_debugx("initiator didn't authenticate, but target " 251 "\"%s\" requires CHAP; skipping", targ->t_name); 252 return (true); 253 } 254 255 assert(conn->conn_user != NULL); 256 auth = auth_find(ag, conn->conn_user); 257 if (auth == NULL) { 258 log_debugx("CHAP user \"%s\" doesn't match target " 259 "\"%s\"; skipping", conn->conn_user, targ->t_name); 260 return (true); 261 } 262 263 error = chap_authenticate(conn->conn_chap, auth->a_secret); 264 if (error != 0) { 265 log_debugx("password for CHAP user \"%s\" doesn't " 266 "match target \"%s\"; skipping", 267 conn->conn_user, targ->t_name); 268 return (true); 269 } 270 } 271 272 return (false); 273 } 274 275 void 276 discovery(struct connection *conn) 277 { 278 struct pdu *request, *response; 279 struct keys *request_keys, *response_keys; 280 const struct port *port; 281 const struct portal_group *pg; 282 const char *send_targets; 283 284 pg = conn->conn_portal->p_portal_group; 285 286 log_debugx("beginning discovery session; waiting for Text PDU"); 287 request = text_receive(conn); 288 request_keys = keys_new(); 289 keys_load(request_keys, request); 290 291 send_targets = keys_find(request_keys, "SendTargets"); 292 if (send_targets == NULL) 293 log_errx(1, "received Text PDU without SendTargets"); 294 295 response = text_new_response(request); 296 response_keys = keys_new(); 297 298 if (strcmp(send_targets, "All") == 0) { 299 TAILQ_FOREACH(port, &pg->pg_ports, p_pgs) { 300 if (discovery_target_filtered_out(conn, port)) { 301 /* Ignore this target. */ 302 continue; 303 } 304 discovery_add_target(response_keys, port->p_target); 305 } 306 } else { 307 port = port_find_in_pg(pg, send_targets); 308 if (port == NULL) { 309 log_debugx("initiator requested information on unknown " 310 "target \"%s\"; returning nothing", send_targets); 311 } else { 312 if (discovery_target_filtered_out(conn, port)) { 313 /* Ignore this target. */ 314 } else { 315 discovery_add_target(response_keys, port->p_target); 316 } 317 } 318 } 319 keys_save(response_keys, response); 320 321 pdu_send(response); 322 pdu_delete(response); 323 keys_delete(response_keys); 324 pdu_delete(request); 325 keys_delete(request_keys); 326 327 log_debugx("done sending targets; waiting for Logout PDU"); 328 request = logout_receive(conn); 329 response = logout_new_response(request); 330 331 pdu_send(response); 332 pdu_delete(response); 333 pdu_delete(request); 334 335 log_debugx("discovery session done"); 336 } 337