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