xref: /freebsd/usr.sbin/ctld/login.cc (revision ce8cfd998b4fc01cfd47e009b0f6dead404841b4)
1839d0755SJohn Baldwin /*-
2839d0755SJohn Baldwin  * SPDX-License-Identifier: BSD-2-Clause
3839d0755SJohn Baldwin  *
4839d0755SJohn Baldwin  * Copyright (c) 2012 The FreeBSD Foundation
5839d0755SJohn Baldwin  *
6839d0755SJohn Baldwin  * This software was developed by Edward Tomasz Napierala under sponsorship
7839d0755SJohn Baldwin  * from the FreeBSD Foundation.
8839d0755SJohn Baldwin  *
9839d0755SJohn Baldwin  * Redistribution and use in source and binary forms, with or without
10839d0755SJohn Baldwin  * modification, are permitted provided that the following conditions
11839d0755SJohn Baldwin  * are met:
12839d0755SJohn Baldwin  * 1. Redistributions of source code must retain the above copyright
13839d0755SJohn Baldwin  *    notice, this list of conditions and the following disclaimer.
14839d0755SJohn Baldwin  * 2. Redistributions in binary form must reproduce the above copyright
15839d0755SJohn Baldwin  *    notice, this list of conditions and the following disclaimer in the
16839d0755SJohn Baldwin  *    documentation and/or other materials provided with the distribution.
17839d0755SJohn Baldwin  *
18839d0755SJohn Baldwin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19839d0755SJohn Baldwin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20839d0755SJohn Baldwin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21839d0755SJohn Baldwin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22839d0755SJohn Baldwin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23839d0755SJohn Baldwin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24839d0755SJohn Baldwin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25839d0755SJohn Baldwin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26839d0755SJohn Baldwin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27839d0755SJohn Baldwin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28839d0755SJohn Baldwin  * SUCH DAMAGE.
29839d0755SJohn Baldwin  *
30839d0755SJohn Baldwin  */
31839d0755SJohn Baldwin 
32839d0755SJohn Baldwin #include <sys/time.h>
33839d0755SJohn Baldwin #include <assert.h>
34839d0755SJohn Baldwin #include <stdbool.h>
35839d0755SJohn Baldwin #include <stdio.h>
36839d0755SJohn Baldwin #include <stdlib.h>
37839d0755SJohn Baldwin #include <string.h>
38839d0755SJohn Baldwin #include <unistd.h>
39839d0755SJohn Baldwin #include <netinet/in.h>
40839d0755SJohn Baldwin #include <cam/ctl/ctl.h>
41839d0755SJohn Baldwin #include <cam/ctl/ctl_io.h>
42839d0755SJohn Baldwin #include <cam/ctl/ctl_ioctl.h>
43839d0755SJohn Baldwin 
44839d0755SJohn Baldwin #include "ctld.h"
45839d0755SJohn Baldwin #include "iscsi_proto.h"
46839d0755SJohn Baldwin 
47839d0755SJohn Baldwin #define	MAX_DATA_SEGMENT_LENGTH		(128 * 1024)
48839d0755SJohn Baldwin 
49839d0755SJohn Baldwin static void login_send_error(struct pdu *request,
50839d0755SJohn Baldwin     char error_class, char detail);
51839d0755SJohn Baldwin 
52839d0755SJohn Baldwin static void
kernel_limits(const char * offload,int s,int * max_recv_dsl,int * max_send_dsl,int * max_burst_length,int * first_burst_length)53839d0755SJohn Baldwin kernel_limits(const char *offload, int s, int *max_recv_dsl, int *max_send_dsl,
54839d0755SJohn Baldwin     int *max_burst_length, int *first_burst_length)
55839d0755SJohn Baldwin {
56839d0755SJohn Baldwin 	struct ctl_iscsi req;
57839d0755SJohn Baldwin 	struct ctl_iscsi_limits_params *cilp;
58839d0755SJohn Baldwin 
59839d0755SJohn Baldwin 	bzero(&req, sizeof(req));
60839d0755SJohn Baldwin 
61839d0755SJohn Baldwin 	req.type = CTL_ISCSI_LIMITS;
62839d0755SJohn Baldwin 	cilp = (struct ctl_iscsi_limits_params *)&(req.data.limits);
63839d0755SJohn Baldwin 	if (offload != NULL) {
64839d0755SJohn Baldwin 		strlcpy(cilp->offload, offload, sizeof(cilp->offload));
65839d0755SJohn Baldwin 	}
66839d0755SJohn Baldwin 	cilp->socket = s;
67839d0755SJohn Baldwin 
68839d0755SJohn Baldwin 	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
69839d0755SJohn Baldwin 		log_err(1, "error issuing CTL_ISCSI ioctl; "
70839d0755SJohn Baldwin 		    "dropping connection");
71839d0755SJohn Baldwin 	}
72839d0755SJohn Baldwin 
73839d0755SJohn Baldwin 	if (req.status != CTL_ISCSI_OK) {
74839d0755SJohn Baldwin 		log_errx(1, "error returned from CTL iSCSI limits request: "
75839d0755SJohn Baldwin 		    "%s; dropping connection", req.error_str);
76839d0755SJohn Baldwin 	}
77839d0755SJohn Baldwin 
78839d0755SJohn Baldwin 	if (cilp->max_recv_data_segment_length != 0) {
79839d0755SJohn Baldwin 		*max_recv_dsl = cilp->max_recv_data_segment_length;
80839d0755SJohn Baldwin 		*max_send_dsl = cilp->max_recv_data_segment_length;
81839d0755SJohn Baldwin 	}
82839d0755SJohn Baldwin 	if (cilp->max_send_data_segment_length != 0)
83839d0755SJohn Baldwin 		*max_send_dsl = cilp->max_send_data_segment_length;
84839d0755SJohn Baldwin 	if (cilp->max_burst_length != 0)
85839d0755SJohn Baldwin 		*max_burst_length = cilp->max_burst_length;
86839d0755SJohn Baldwin 	if (cilp->first_burst_length != 0)
87839d0755SJohn Baldwin 		*first_burst_length = cilp->first_burst_length;
88839d0755SJohn Baldwin 	if (*max_burst_length < *first_burst_length)
89839d0755SJohn Baldwin 		*first_burst_length = *max_burst_length;
90839d0755SJohn Baldwin 
91839d0755SJohn Baldwin 	if (offload != NULL) {
92839d0755SJohn Baldwin 		log_debugx("Kernel limits for offload \"%s\" are "
93839d0755SJohn Baldwin 		    "MaxRecvDataSegment=%d, max_send_dsl=%d, "
94839d0755SJohn Baldwin 		    "MaxBurstLength=%d, FirstBurstLength=%d",
95839d0755SJohn Baldwin 		    offload, *max_recv_dsl, *max_send_dsl, *max_burst_length,
96839d0755SJohn Baldwin 		    *first_burst_length);
97839d0755SJohn Baldwin 	} else {
98839d0755SJohn Baldwin 		log_debugx("Kernel limits are "
99839d0755SJohn Baldwin 		    "MaxRecvDataSegment=%d, max_send_dsl=%d, "
100839d0755SJohn Baldwin 		    "MaxBurstLength=%d, FirstBurstLength=%d",
101839d0755SJohn Baldwin 		    *max_recv_dsl, *max_send_dsl, *max_burst_length,
102839d0755SJohn Baldwin 		    *first_burst_length);
103839d0755SJohn Baldwin 	}
104839d0755SJohn Baldwin }
105839d0755SJohn Baldwin 
106839d0755SJohn Baldwin static void
login_set_nsg(struct pdu * response,int nsg)107839d0755SJohn Baldwin login_set_nsg(struct pdu *response, int nsg)
108839d0755SJohn Baldwin {
109839d0755SJohn Baldwin 	struct iscsi_bhs_login_response *bhslr;
110839d0755SJohn Baldwin 
111839d0755SJohn Baldwin 	assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
112839d0755SJohn Baldwin 	    nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
113839d0755SJohn Baldwin 	    nsg == BHSLR_STAGE_FULL_FEATURE_PHASE);
114839d0755SJohn Baldwin 
115839d0755SJohn Baldwin 	bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
116839d0755SJohn Baldwin 
117839d0755SJohn Baldwin 	bhslr->bhslr_flags &= 0xFC;
118839d0755SJohn Baldwin 	bhslr->bhslr_flags |= nsg;
119839d0755SJohn Baldwin 	bhslr->bhslr_flags |= BHSLR_FLAGS_TRANSIT;
120839d0755SJohn Baldwin }
121839d0755SJohn Baldwin 
122839d0755SJohn Baldwin static int
login_csg(const struct pdu * request)123839d0755SJohn Baldwin login_csg(const struct pdu *request)
124839d0755SJohn Baldwin {
125839d0755SJohn Baldwin 	struct iscsi_bhs_login_request *bhslr;
126839d0755SJohn Baldwin 
127839d0755SJohn Baldwin 	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
128839d0755SJohn Baldwin 
129839d0755SJohn Baldwin 	return ((bhslr->bhslr_flags & 0x0C) >> 2);
130839d0755SJohn Baldwin }
131839d0755SJohn Baldwin 
132839d0755SJohn Baldwin static void
login_set_csg(struct pdu * response,int csg)133839d0755SJohn Baldwin login_set_csg(struct pdu *response, int csg)
134839d0755SJohn Baldwin {
135839d0755SJohn Baldwin 	struct iscsi_bhs_login_response *bhslr;
136839d0755SJohn Baldwin 
137839d0755SJohn Baldwin 	assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
138839d0755SJohn Baldwin 	    csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
139839d0755SJohn Baldwin 	    csg == BHSLR_STAGE_FULL_FEATURE_PHASE);
140839d0755SJohn Baldwin 
141839d0755SJohn Baldwin 	bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
142839d0755SJohn Baldwin 
143839d0755SJohn Baldwin 	bhslr->bhslr_flags &= 0xF3;
144839d0755SJohn Baldwin 	bhslr->bhslr_flags |= csg << 2;
145839d0755SJohn Baldwin }
146839d0755SJohn Baldwin 
147839d0755SJohn Baldwin static struct pdu *
login_receive(struct connection * conn,bool initial)148839d0755SJohn Baldwin login_receive(struct connection *conn, bool initial)
149839d0755SJohn Baldwin {
150839d0755SJohn Baldwin 	struct pdu *request;
151839d0755SJohn Baldwin 	struct iscsi_bhs_login_request *bhslr;
152839d0755SJohn Baldwin 
153839d0755SJohn Baldwin 	request = pdu_new(conn);
154839d0755SJohn Baldwin 	pdu_receive(request);
155839d0755SJohn Baldwin 	if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
156839d0755SJohn Baldwin 	    ISCSI_BHS_OPCODE_LOGIN_REQUEST) {
157839d0755SJohn Baldwin 		/*
158839d0755SJohn Baldwin 		 * The first PDU in session is special - if we receive any PDU
159839d0755SJohn Baldwin 		 * different than login request, we have to drop the connection
160839d0755SJohn Baldwin 		 * without sending response ("A target receiving any PDU
161839d0755SJohn Baldwin 		 * except a Login request before the Login Phase is started MUST
162839d0755SJohn Baldwin 		 * immediately terminate the connection on which the PDU
163839d0755SJohn Baldwin 		 * was received.")
164839d0755SJohn Baldwin 		 */
165839d0755SJohn Baldwin 		if (initial == false)
166839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x0b);
167839d0755SJohn Baldwin 		log_errx(1, "protocol error: received invalid opcode 0x%x",
168839d0755SJohn Baldwin 		    request->pdu_bhs->bhs_opcode);
169839d0755SJohn Baldwin 	}
170839d0755SJohn Baldwin 	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
171839d0755SJohn Baldwin 	/*
172839d0755SJohn Baldwin 	 * XXX: Implement the C flag some day.
173839d0755SJohn Baldwin 	 */
174839d0755SJohn Baldwin 	if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) {
175839d0755SJohn Baldwin 		login_send_error(request, 0x03, 0x00);
176839d0755SJohn Baldwin 		log_errx(1, "received Login PDU with unsupported \"C\" flag");
177839d0755SJohn Baldwin 	}
178839d0755SJohn Baldwin 	if (bhslr->bhslr_version_max != 0x00) {
179839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x05);
180839d0755SJohn Baldwin 		log_errx(1, "received Login PDU with unsupported "
181839d0755SJohn Baldwin 		    "Version-max 0x%x", bhslr->bhslr_version_max);
182839d0755SJohn Baldwin 	}
183839d0755SJohn Baldwin 	if (bhslr->bhslr_version_min != 0x00) {
184839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x05);
185839d0755SJohn Baldwin 		log_errx(1, "received Login PDU with unsupported "
186839d0755SJohn Baldwin 		    "Version-min 0x%x", bhslr->bhslr_version_min);
187839d0755SJohn Baldwin 	}
188839d0755SJohn Baldwin 	if (initial == false &&
189839d0755SJohn Baldwin 	    ISCSI_SNLT(ntohl(bhslr->bhslr_cmdsn), conn->conn_cmdsn)) {
190839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x00);
191839d0755SJohn Baldwin 		log_errx(1, "received Login PDU with decreasing CmdSN: "
192839d0755SJohn Baldwin 		    "was %u, is %u", conn->conn_cmdsn,
193839d0755SJohn Baldwin 		    ntohl(bhslr->bhslr_cmdsn));
194839d0755SJohn Baldwin 	}
195839d0755SJohn Baldwin 	if (initial == false &&
196839d0755SJohn Baldwin 	    ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) {
197839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x00);
198839d0755SJohn Baldwin 		log_errx(1, "received Login PDU with wrong ExpStatSN: "
199839d0755SJohn Baldwin 		    "is %u, should be %u", ntohl(bhslr->bhslr_expstatsn),
200839d0755SJohn Baldwin 		    conn->conn_statsn);
201839d0755SJohn Baldwin 	}
202839d0755SJohn Baldwin 	conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn);
203839d0755SJohn Baldwin 
204839d0755SJohn Baldwin 	return (request);
205839d0755SJohn Baldwin }
206839d0755SJohn Baldwin 
207839d0755SJohn Baldwin static struct pdu *
login_new_response(struct pdu * request)208839d0755SJohn Baldwin login_new_response(struct pdu *request)
209839d0755SJohn Baldwin {
210839d0755SJohn Baldwin 	struct pdu *response;
211839d0755SJohn Baldwin 	struct connection *conn;
212839d0755SJohn Baldwin 	struct iscsi_bhs_login_request *bhslr;
213839d0755SJohn Baldwin 	struct iscsi_bhs_login_response *bhslr2;
214839d0755SJohn Baldwin 
215839d0755SJohn Baldwin 	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
216839d0755SJohn Baldwin 	conn = request->pdu_connection;
217839d0755SJohn Baldwin 
218839d0755SJohn Baldwin 	response = pdu_new_response(request);
219839d0755SJohn Baldwin 	bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
220839d0755SJohn Baldwin 	bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_RESPONSE;
221839d0755SJohn Baldwin 	login_set_csg(response, BHSLR_STAGE_SECURITY_NEGOTIATION);
222839d0755SJohn Baldwin 	memcpy(bhslr2->bhslr_isid,
223839d0755SJohn Baldwin 	    bhslr->bhslr_isid, sizeof(bhslr2->bhslr_isid));
224839d0755SJohn Baldwin 	bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag;
225839d0755SJohn Baldwin 	bhslr2->bhslr_statsn = htonl(conn->conn_statsn++);
226839d0755SJohn Baldwin 	bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn);
227839d0755SJohn Baldwin 	bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn);
228839d0755SJohn Baldwin 
229839d0755SJohn Baldwin 	return (response);
230839d0755SJohn Baldwin }
231839d0755SJohn Baldwin 
232839d0755SJohn Baldwin static void
login_send_error(struct pdu * request,char error_class,char detail)233839d0755SJohn Baldwin login_send_error(struct pdu *request, char error_class, char detail)
234839d0755SJohn Baldwin {
235839d0755SJohn Baldwin 	struct pdu *response;
236839d0755SJohn Baldwin 	struct iscsi_bhs_login_response *bhslr2;
237839d0755SJohn Baldwin 
238839d0755SJohn Baldwin 	log_debugx("sending Login Response PDU with failure class 0x%x/0x%x; "
239839d0755SJohn Baldwin 	    "see next line for reason", error_class, detail);
240839d0755SJohn Baldwin 	response = login_new_response(request);
241839d0755SJohn Baldwin 	bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
242839d0755SJohn Baldwin 	bhslr2->bhslr_status_class = error_class;
243839d0755SJohn Baldwin 	bhslr2->bhslr_status_detail = detail;
244839d0755SJohn Baldwin 
245839d0755SJohn Baldwin 	pdu_send(response);
246839d0755SJohn Baldwin 	pdu_delete(response);
247839d0755SJohn Baldwin }
248839d0755SJohn Baldwin 
249839d0755SJohn Baldwin static int
login_list_contains(const char * list,const char * what)250839d0755SJohn Baldwin login_list_contains(const char *list, const char *what)
251839d0755SJohn Baldwin {
252839d0755SJohn Baldwin 	char *tofree, *str, *token;
253839d0755SJohn Baldwin 
254839d0755SJohn Baldwin 	tofree = str = checked_strdup(list);
255839d0755SJohn Baldwin 
256839d0755SJohn Baldwin 	while ((token = strsep(&str, ",")) != NULL) {
257839d0755SJohn Baldwin 		if (strcmp(token, what) == 0) {
258839d0755SJohn Baldwin 			free(tofree);
259839d0755SJohn Baldwin 			return (1);
260839d0755SJohn Baldwin 		}
261839d0755SJohn Baldwin 	}
262839d0755SJohn Baldwin 	free(tofree);
263839d0755SJohn Baldwin 	return (0);
264839d0755SJohn Baldwin }
265839d0755SJohn Baldwin 
266839d0755SJohn Baldwin static int
login_list_prefers(const char * list,const char * choice1,const char * choice2)267839d0755SJohn Baldwin login_list_prefers(const char *list,
268839d0755SJohn Baldwin     const char *choice1, const char *choice2)
269839d0755SJohn Baldwin {
270839d0755SJohn Baldwin 	char *tofree, *str, *token;
271839d0755SJohn Baldwin 
272839d0755SJohn Baldwin 	tofree = str = checked_strdup(list);
273839d0755SJohn Baldwin 
274839d0755SJohn Baldwin 	while ((token = strsep(&str, ",")) != NULL) {
275839d0755SJohn Baldwin 		if (strcmp(token, choice1) == 0) {
276839d0755SJohn Baldwin 			free(tofree);
277839d0755SJohn Baldwin 			return (1);
278839d0755SJohn Baldwin 		}
279839d0755SJohn Baldwin 		if (strcmp(token, choice2) == 0) {
280839d0755SJohn Baldwin 			free(tofree);
281839d0755SJohn Baldwin 			return (2);
282839d0755SJohn Baldwin 		}
283839d0755SJohn Baldwin 	}
284839d0755SJohn Baldwin 	free(tofree);
285839d0755SJohn Baldwin 	return (-1);
286839d0755SJohn Baldwin }
287839d0755SJohn Baldwin 
288839d0755SJohn Baldwin static struct pdu *
login_receive_chap_a(struct connection * conn)289839d0755SJohn Baldwin login_receive_chap_a(struct connection *conn)
290839d0755SJohn Baldwin {
291839d0755SJohn Baldwin 	struct pdu *request;
292839d0755SJohn Baldwin 	struct keys *request_keys;
293839d0755SJohn Baldwin 	const char *chap_a;
294839d0755SJohn Baldwin 
295839d0755SJohn Baldwin 	request = login_receive(conn, false);
296839d0755SJohn Baldwin 	request_keys = keys_new();
297839d0755SJohn Baldwin 	keys_load_pdu(request_keys, request);
298839d0755SJohn Baldwin 
299839d0755SJohn Baldwin 	chap_a = keys_find(request_keys, "CHAP_A");
300839d0755SJohn Baldwin 	if (chap_a == NULL) {
301839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x07);
302839d0755SJohn Baldwin 		log_errx(1, "received CHAP Login PDU without CHAP_A");
303839d0755SJohn Baldwin 	}
304839d0755SJohn Baldwin 	if (login_list_contains(chap_a, "5") == 0) {
305839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x01);
306839d0755SJohn Baldwin 		log_errx(1, "received CHAP Login PDU with unsupported CHAP_A "
307839d0755SJohn Baldwin 		    "\"%s\"", chap_a);
308839d0755SJohn Baldwin 	}
309839d0755SJohn Baldwin 	keys_delete(request_keys);
310839d0755SJohn Baldwin 
311839d0755SJohn Baldwin 	return (request);
312839d0755SJohn Baldwin }
313839d0755SJohn Baldwin 
314839d0755SJohn Baldwin static void
login_send_chap_c(struct pdu * request,struct chap * chap)315839d0755SJohn Baldwin login_send_chap_c(struct pdu *request, struct chap *chap)
316839d0755SJohn Baldwin {
317839d0755SJohn Baldwin 	struct pdu *response;
318839d0755SJohn Baldwin 	struct keys *response_keys;
319839d0755SJohn Baldwin 	char *chap_c, *chap_i;
320839d0755SJohn Baldwin 
321839d0755SJohn Baldwin 	chap_c = chap_get_challenge(chap);
322839d0755SJohn Baldwin 	chap_i = chap_get_id(chap);
323839d0755SJohn Baldwin 
324839d0755SJohn Baldwin 	response = login_new_response(request);
325839d0755SJohn Baldwin 	response_keys = keys_new();
326839d0755SJohn Baldwin 	keys_add(response_keys, "CHAP_A", "5");
327839d0755SJohn Baldwin 	keys_add(response_keys, "CHAP_I", chap_i);
328839d0755SJohn Baldwin 	keys_add(response_keys, "CHAP_C", chap_c);
329839d0755SJohn Baldwin 	free(chap_i);
330839d0755SJohn Baldwin 	free(chap_c);
331839d0755SJohn Baldwin 	keys_save_pdu(response_keys, response);
332839d0755SJohn Baldwin 	pdu_send(response);
333839d0755SJohn Baldwin 	pdu_delete(response);
334839d0755SJohn Baldwin 	keys_delete(response_keys);
335839d0755SJohn Baldwin }
336839d0755SJohn Baldwin 
337839d0755SJohn Baldwin static struct pdu *
login_receive_chap_r(struct connection * conn,struct auth_group * ag,struct chap * chap,const struct auth ** authp)338839d0755SJohn Baldwin login_receive_chap_r(struct connection *conn, struct auth_group *ag,
339839d0755SJohn Baldwin     struct chap *chap, const struct auth **authp)
340839d0755SJohn Baldwin {
341839d0755SJohn Baldwin 	struct pdu *request;
342839d0755SJohn Baldwin 	struct keys *request_keys;
343839d0755SJohn Baldwin 	const char *chap_n, *chap_r;
344839d0755SJohn Baldwin 	const struct auth *auth;
345839d0755SJohn Baldwin 	int error;
346839d0755SJohn Baldwin 
347839d0755SJohn Baldwin 	request = login_receive(conn, false);
348839d0755SJohn Baldwin 	request_keys = keys_new();
349839d0755SJohn Baldwin 	keys_load_pdu(request_keys, request);
350839d0755SJohn Baldwin 
351839d0755SJohn Baldwin 	chap_n = keys_find(request_keys, "CHAP_N");
352839d0755SJohn Baldwin 	if (chap_n == NULL) {
353839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x07);
354839d0755SJohn Baldwin 		log_errx(1, "received CHAP Login PDU without CHAP_N");
355839d0755SJohn Baldwin 	}
356839d0755SJohn Baldwin 	chap_r = keys_find(request_keys, "CHAP_R");
357839d0755SJohn Baldwin 	if (chap_r == NULL) {
358839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x07);
359839d0755SJohn Baldwin 		log_errx(1, "received CHAP Login PDU without CHAP_R");
360839d0755SJohn Baldwin 	}
361839d0755SJohn Baldwin 	error = chap_receive(chap, chap_r);
362839d0755SJohn Baldwin 	if (error != 0) {
363839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x07);
364839d0755SJohn Baldwin 		log_errx(1, "received CHAP Login PDU with malformed CHAP_R");
365839d0755SJohn Baldwin 	}
366839d0755SJohn Baldwin 
367839d0755SJohn Baldwin 	/*
368839d0755SJohn Baldwin 	 * Verify the response.
369839d0755SJohn Baldwin 	 */
370839d0755SJohn Baldwin 	assert(ag->ag_type == AG_TYPE_CHAP ||
371839d0755SJohn Baldwin 	    ag->ag_type == AG_TYPE_CHAP_MUTUAL);
372839d0755SJohn Baldwin 	auth = auth_find(ag, chap_n);
373839d0755SJohn Baldwin 	if (auth == NULL) {
374839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x01);
375839d0755SJohn Baldwin 		log_errx(1, "received CHAP Login with invalid user \"%s\"",
376839d0755SJohn Baldwin 		    chap_n);
377839d0755SJohn Baldwin 	}
378839d0755SJohn Baldwin 
379839d0755SJohn Baldwin 	assert(auth->a_secret != NULL);
380839d0755SJohn Baldwin 	assert(strlen(auth->a_secret) > 0);
381839d0755SJohn Baldwin 
382839d0755SJohn Baldwin 	error = chap_authenticate(chap, auth->a_secret);
383839d0755SJohn Baldwin 	if (error != 0) {
384839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x01);
385839d0755SJohn Baldwin 		log_errx(1, "CHAP authentication failed for user \"%s\"",
386839d0755SJohn Baldwin 		    auth->a_user);
387839d0755SJohn Baldwin 	}
388839d0755SJohn Baldwin 
389839d0755SJohn Baldwin 	keys_delete(request_keys);
390839d0755SJohn Baldwin 
391839d0755SJohn Baldwin 	*authp = auth;
392839d0755SJohn Baldwin 	return (request);
393839d0755SJohn Baldwin }
394839d0755SJohn Baldwin 
395839d0755SJohn Baldwin static void
login_send_chap_success(struct pdu * request,const struct auth * auth)396839d0755SJohn Baldwin login_send_chap_success(struct pdu *request,
397839d0755SJohn Baldwin     const struct auth *auth)
398839d0755SJohn Baldwin {
399839d0755SJohn Baldwin 	struct pdu *response;
400839d0755SJohn Baldwin 	struct keys *request_keys, *response_keys;
401839d0755SJohn Baldwin 	struct rchap *rchap;
402839d0755SJohn Baldwin 	const char *chap_i, *chap_c;
403839d0755SJohn Baldwin 	char *chap_r;
404839d0755SJohn Baldwin 	int error;
405839d0755SJohn Baldwin 
406839d0755SJohn Baldwin 	response = login_new_response(request);
407839d0755SJohn Baldwin 	login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
408839d0755SJohn Baldwin 
409839d0755SJohn Baldwin 	/*
410839d0755SJohn Baldwin 	 * Actually, one more thing: mutual authentication.
411839d0755SJohn Baldwin 	 */
412839d0755SJohn Baldwin 	request_keys = keys_new();
413839d0755SJohn Baldwin 	keys_load_pdu(request_keys, request);
414839d0755SJohn Baldwin 	chap_i = keys_find(request_keys, "CHAP_I");
415839d0755SJohn Baldwin 	chap_c = keys_find(request_keys, "CHAP_C");
416839d0755SJohn Baldwin 	if (chap_i != NULL || chap_c != NULL) {
417839d0755SJohn Baldwin 		if (chap_i == NULL) {
418839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x07);
419839d0755SJohn Baldwin 			log_errx(1, "initiator requested target "
420839d0755SJohn Baldwin 			    "authentication, but didn't send CHAP_I");
421839d0755SJohn Baldwin 		}
422839d0755SJohn Baldwin 		if (chap_c == NULL) {
423839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x07);
424839d0755SJohn Baldwin 			log_errx(1, "initiator requested target "
425839d0755SJohn Baldwin 			    "authentication, but didn't send CHAP_C");
426839d0755SJohn Baldwin 		}
427839d0755SJohn Baldwin 		if (auth->a_auth_group->ag_type != AG_TYPE_CHAP_MUTUAL) {
428839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x01);
429839d0755SJohn Baldwin 			log_errx(1, "initiator requests target authentication "
430839d0755SJohn Baldwin 			    "for user \"%s\", but mutual user/secret "
431839d0755SJohn Baldwin 			    "is not set", auth->a_user);
432839d0755SJohn Baldwin 		}
433839d0755SJohn Baldwin 
434839d0755SJohn Baldwin 		log_debugx("performing mutual authentication as user \"%s\"",
435839d0755SJohn Baldwin 		    auth->a_mutual_user);
436839d0755SJohn Baldwin 
437839d0755SJohn Baldwin 		rchap = rchap_new(auth->a_mutual_secret);
438839d0755SJohn Baldwin 		error = rchap_receive(rchap, chap_i, chap_c);
439839d0755SJohn Baldwin 		if (error != 0) {
440839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x07);
441839d0755SJohn Baldwin 			log_errx(1, "received CHAP Login PDU with malformed "
442839d0755SJohn Baldwin 			    "CHAP_I or CHAP_C");
443839d0755SJohn Baldwin 		}
444839d0755SJohn Baldwin 		chap_r = rchap_get_response(rchap);
445839d0755SJohn Baldwin 		rchap_delete(rchap);
446839d0755SJohn Baldwin 		response_keys = keys_new();
447839d0755SJohn Baldwin 		keys_add(response_keys, "CHAP_N", auth->a_mutual_user);
448839d0755SJohn Baldwin 		keys_add(response_keys, "CHAP_R", chap_r);
449839d0755SJohn Baldwin 		free(chap_r);
450839d0755SJohn Baldwin 		keys_save_pdu(response_keys, response);
451839d0755SJohn Baldwin 		keys_delete(response_keys);
452839d0755SJohn Baldwin 	} else {
453839d0755SJohn Baldwin 		log_debugx("initiator did not request target authentication");
454839d0755SJohn Baldwin 	}
455839d0755SJohn Baldwin 
456839d0755SJohn Baldwin 	keys_delete(request_keys);
457839d0755SJohn Baldwin 	pdu_send(response);
458839d0755SJohn Baldwin 	pdu_delete(response);
459839d0755SJohn Baldwin }
460839d0755SJohn Baldwin 
461839d0755SJohn Baldwin static void
login_chap(struct ctld_connection * conn,struct auth_group * ag)462839d0755SJohn Baldwin login_chap(struct ctld_connection *conn, struct auth_group *ag)
463839d0755SJohn Baldwin {
464839d0755SJohn Baldwin 	const struct auth *auth;
465839d0755SJohn Baldwin 	struct chap *chap;
466839d0755SJohn Baldwin 	struct pdu *request;
467839d0755SJohn Baldwin 
468839d0755SJohn Baldwin 	/*
469839d0755SJohn Baldwin 	 * Receive CHAP_A PDU.
470839d0755SJohn Baldwin 	 */
471839d0755SJohn Baldwin 	log_debugx("beginning CHAP authentication; waiting for CHAP_A");
472839d0755SJohn Baldwin 	request = login_receive_chap_a(&conn->conn);
473839d0755SJohn Baldwin 
474839d0755SJohn Baldwin 	/*
475839d0755SJohn Baldwin 	 * Generate the challenge.
476839d0755SJohn Baldwin 	 */
477839d0755SJohn Baldwin 	chap = chap_new();
478839d0755SJohn Baldwin 
479839d0755SJohn Baldwin 	/*
480839d0755SJohn Baldwin 	 * Send the challenge.
481839d0755SJohn Baldwin 	 */
482839d0755SJohn Baldwin 	log_debugx("sending CHAP_C, binary challenge size is %zd bytes",
483839d0755SJohn Baldwin 	    sizeof(chap->chap_challenge));
484839d0755SJohn Baldwin 	login_send_chap_c(request, chap);
485839d0755SJohn Baldwin 	pdu_delete(request);
486839d0755SJohn Baldwin 
487839d0755SJohn Baldwin 	/*
488839d0755SJohn Baldwin 	 * Receive CHAP_N/CHAP_R PDU and authenticate.
489839d0755SJohn Baldwin 	 */
490839d0755SJohn Baldwin 	log_debugx("waiting for CHAP_N/CHAP_R");
491839d0755SJohn Baldwin 	request = login_receive_chap_r(&conn->conn, ag, chap, &auth);
492839d0755SJohn Baldwin 
493839d0755SJohn Baldwin 	/*
494839d0755SJohn Baldwin 	 * Yay, authentication succeeded!
495839d0755SJohn Baldwin 	 */
496839d0755SJohn Baldwin 	log_debugx("authentication succeeded for user \"%s\"; "
497839d0755SJohn Baldwin 	    "transitioning to operational parameter negotiation", auth->a_user);
498839d0755SJohn Baldwin 	login_send_chap_success(request, auth);
499839d0755SJohn Baldwin 	pdu_delete(request);
500839d0755SJohn Baldwin 
501839d0755SJohn Baldwin 	/*
502839d0755SJohn Baldwin 	 * Leave username and CHAP information for discovery().
503839d0755SJohn Baldwin 	 */
504839d0755SJohn Baldwin 	conn->conn_user = auth->a_user;
505839d0755SJohn Baldwin 	conn->conn_chap = chap;
506839d0755SJohn Baldwin }
507839d0755SJohn Baldwin 
508839d0755SJohn Baldwin static void
login_negotiate_key(struct pdu * request,const char * name,const char * value,bool skipped_security,struct keys * response_keys)509839d0755SJohn Baldwin login_negotiate_key(struct pdu *request, const char *name,
510839d0755SJohn Baldwin     const char *value, bool skipped_security, struct keys *response_keys)
511839d0755SJohn Baldwin {
512839d0755SJohn Baldwin 	int which;
513839d0755SJohn Baldwin 	size_t tmp;
514839d0755SJohn Baldwin 	struct ctld_connection *conn;
515839d0755SJohn Baldwin 
516839d0755SJohn Baldwin 	conn = (struct ctld_connection *)request->pdu_connection;
517839d0755SJohn Baldwin 
518839d0755SJohn Baldwin 	if (strcmp(name, "InitiatorName") == 0) {
519839d0755SJohn Baldwin 		if (!skipped_security)
520839d0755SJohn Baldwin 			log_errx(1, "initiator resent InitiatorName");
521839d0755SJohn Baldwin 	} else if (strcmp(name, "SessionType") == 0) {
522839d0755SJohn Baldwin 		if (!skipped_security)
523839d0755SJohn Baldwin 			log_errx(1, "initiator resent SessionType");
524839d0755SJohn Baldwin 	} else if (strcmp(name, "TargetName") == 0) {
525839d0755SJohn Baldwin 		if (!skipped_security)
526839d0755SJohn Baldwin 			log_errx(1, "initiator resent TargetName");
527839d0755SJohn Baldwin 	} else if (strcmp(name, "InitiatorAlias") == 0) {
528839d0755SJohn Baldwin 		if (conn->conn_initiator_alias != NULL)
529839d0755SJohn Baldwin 			free(conn->conn_initiator_alias);
530839d0755SJohn Baldwin 		conn->conn_initiator_alias = checked_strdup(value);
531839d0755SJohn Baldwin 	} else if (strcmp(value, "Irrelevant") == 0) {
532839d0755SJohn Baldwin 		/* Ignore. */
533839d0755SJohn Baldwin 	} else if (strcmp(name, "HeaderDigest") == 0) {
534839d0755SJohn Baldwin 		/*
535839d0755SJohn Baldwin 		 * We don't handle digests for discovery sessions.
536839d0755SJohn Baldwin 		 */
537839d0755SJohn Baldwin 		if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) {
538839d0755SJohn Baldwin 			log_debugx("discovery session; digests disabled");
539839d0755SJohn Baldwin 			keys_add(response_keys, name, "None");
540839d0755SJohn Baldwin 			return;
541839d0755SJohn Baldwin 		}
542839d0755SJohn Baldwin 
543839d0755SJohn Baldwin 		which = login_list_prefers(value, "CRC32C", "None");
544839d0755SJohn Baldwin 		switch (which) {
545839d0755SJohn Baldwin 		case 1:
546839d0755SJohn Baldwin 			log_debugx("initiator prefers CRC32C "
547839d0755SJohn Baldwin 			    "for header digest; we'll use it");
548839d0755SJohn Baldwin 			conn->conn.conn_header_digest = CONN_DIGEST_CRC32C;
549839d0755SJohn Baldwin 			keys_add(response_keys, name, "CRC32C");
550839d0755SJohn Baldwin 			break;
551839d0755SJohn Baldwin 		case 2:
552839d0755SJohn Baldwin 			log_debugx("initiator prefers not to do "
553839d0755SJohn Baldwin 			    "header digest; we'll comply");
554839d0755SJohn Baldwin 			keys_add(response_keys, name, "None");
555839d0755SJohn Baldwin 			break;
556839d0755SJohn Baldwin 		default:
557839d0755SJohn Baldwin 			log_warnx("initiator sent unrecognized "
558839d0755SJohn Baldwin 			    "HeaderDigest value \"%s\"; will use None", value);
559839d0755SJohn Baldwin 			keys_add(response_keys, name, "None");
560839d0755SJohn Baldwin 			break;
561839d0755SJohn Baldwin 		}
562839d0755SJohn Baldwin 	} else if (strcmp(name, "DataDigest") == 0) {
563839d0755SJohn Baldwin 		if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) {
564839d0755SJohn Baldwin 			log_debugx("discovery session; digests disabled");
565839d0755SJohn Baldwin 			keys_add(response_keys, name, "None");
566839d0755SJohn Baldwin 			return;
567839d0755SJohn Baldwin 		}
568839d0755SJohn Baldwin 
569839d0755SJohn Baldwin 		which = login_list_prefers(value, "CRC32C", "None");
570839d0755SJohn Baldwin 		switch (which) {
571839d0755SJohn Baldwin 		case 1:
572839d0755SJohn Baldwin 			log_debugx("initiator prefers CRC32C "
573839d0755SJohn Baldwin 			    "for data digest; we'll use it");
574839d0755SJohn Baldwin 			conn->conn.conn_data_digest = CONN_DIGEST_CRC32C;
575839d0755SJohn Baldwin 			keys_add(response_keys, name, "CRC32C");
576839d0755SJohn Baldwin 			break;
577839d0755SJohn Baldwin 		case 2:
578839d0755SJohn Baldwin 			log_debugx("initiator prefers not to do "
579839d0755SJohn Baldwin 			    "data digest; we'll comply");
580839d0755SJohn Baldwin 			keys_add(response_keys, name, "None");
581839d0755SJohn Baldwin 			break;
582839d0755SJohn Baldwin 		default:
583839d0755SJohn Baldwin 			log_warnx("initiator sent unrecognized "
584839d0755SJohn Baldwin 			    "DataDigest value \"%s\"; will use None", value);
585839d0755SJohn Baldwin 			keys_add(response_keys, name, "None");
586839d0755SJohn Baldwin 			break;
587839d0755SJohn Baldwin 		}
588839d0755SJohn Baldwin 	} else if (strcmp(name, "MaxConnections") == 0) {
589839d0755SJohn Baldwin 		keys_add(response_keys, name, "1");
590839d0755SJohn Baldwin 	} else if (strcmp(name, "InitialR2T") == 0) {
591839d0755SJohn Baldwin 		keys_add(response_keys, name, "Yes");
592839d0755SJohn Baldwin 	} else if (strcmp(name, "ImmediateData") == 0) {
593839d0755SJohn Baldwin 		if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) {
594839d0755SJohn Baldwin 			log_debugx("discovery session; ImmediateData irrelevant");
595839d0755SJohn Baldwin 			keys_add(response_keys, name, "Irrelevant");
596839d0755SJohn Baldwin 		} else {
597839d0755SJohn Baldwin 			if (strcmp(value, "Yes") == 0) {
598839d0755SJohn Baldwin 				conn->conn.conn_immediate_data = true;
599839d0755SJohn Baldwin 				keys_add(response_keys, name, "Yes");
600839d0755SJohn Baldwin 			} else {
601839d0755SJohn Baldwin 				conn->conn.conn_immediate_data = false;
602839d0755SJohn Baldwin 				keys_add(response_keys, name, "No");
603839d0755SJohn Baldwin 			}
604839d0755SJohn Baldwin 		}
605839d0755SJohn Baldwin 	} else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) {
606839d0755SJohn Baldwin 		tmp = strtoul(value, NULL, 10);
607839d0755SJohn Baldwin 		if (tmp <= 0) {
608839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x00);
609839d0755SJohn Baldwin 			log_errx(1, "received invalid "
610839d0755SJohn Baldwin 			    "MaxRecvDataSegmentLength");
611839d0755SJohn Baldwin 		}
612839d0755SJohn Baldwin 
613839d0755SJohn Baldwin 		/*
614839d0755SJohn Baldwin 		 * MaxRecvDataSegmentLength is a direction-specific parameter.
615839d0755SJohn Baldwin 		 * We'll limit our _send_ to what the initiator can handle but
616839d0755SJohn Baldwin 		 * our MaxRecvDataSegmentLength is not influenced by the
617839d0755SJohn Baldwin 		 * initiator in any way.
618839d0755SJohn Baldwin 		 */
619839d0755SJohn Baldwin 		if ((int)tmp > conn->conn_max_send_data_segment_limit) {
620839d0755SJohn Baldwin 			log_debugx("capping MaxRecvDataSegmentLength "
621839d0755SJohn Baldwin 			    "from %zd to %d", tmp,
622839d0755SJohn Baldwin 			    conn->conn_max_send_data_segment_limit);
623839d0755SJohn Baldwin 			tmp = conn->conn_max_send_data_segment_limit;
624839d0755SJohn Baldwin 		}
625839d0755SJohn Baldwin 		conn->conn.conn_max_send_data_segment_length = tmp;
626839d0755SJohn Baldwin 	} else if (strcmp(name, "MaxBurstLength") == 0) {
627839d0755SJohn Baldwin 		tmp = strtoul(value, NULL, 10);
628839d0755SJohn Baldwin 		if (tmp <= 0) {
629839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x00);
630839d0755SJohn Baldwin 			log_errx(1, "received invalid MaxBurstLength");
631839d0755SJohn Baldwin 		}
632839d0755SJohn Baldwin 		if ((int)tmp > conn->conn_max_burst_limit) {
633839d0755SJohn Baldwin 			log_debugx("capping MaxBurstLength from %zd to %d",
634839d0755SJohn Baldwin 			    tmp, conn->conn_max_burst_limit);
635839d0755SJohn Baldwin 			tmp = conn->conn_max_burst_limit;
636839d0755SJohn Baldwin 		}
637839d0755SJohn Baldwin 		conn->conn.conn_max_burst_length = tmp;
638839d0755SJohn Baldwin 		keys_add_int(response_keys, name, tmp);
639839d0755SJohn Baldwin 	} else if (strcmp(name, "FirstBurstLength") == 0) {
640839d0755SJohn Baldwin 		tmp = strtoul(value, NULL, 10);
641839d0755SJohn Baldwin 		if (tmp <= 0) {
642839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x00);
643839d0755SJohn Baldwin 			log_errx(1, "received invalid FirstBurstLength");
644839d0755SJohn Baldwin 		}
645839d0755SJohn Baldwin 		if ((int)tmp > conn->conn_first_burst_limit) {
646839d0755SJohn Baldwin 			log_debugx("capping FirstBurstLength from %zd to %d",
647839d0755SJohn Baldwin 			    tmp, conn->conn_first_burst_limit);
648839d0755SJohn Baldwin 			tmp = conn->conn_first_burst_limit;
649839d0755SJohn Baldwin 		}
650839d0755SJohn Baldwin 		conn->conn.conn_first_burst_length = tmp;
651839d0755SJohn Baldwin 		keys_add_int(response_keys, name, tmp);
652839d0755SJohn Baldwin 	} else if (strcmp(name, "DefaultTime2Wait") == 0) {
653839d0755SJohn Baldwin 		keys_add(response_keys, name, value);
654839d0755SJohn Baldwin 	} else if (strcmp(name, "DefaultTime2Retain") == 0) {
655839d0755SJohn Baldwin 		keys_add(response_keys, name, "0");
656839d0755SJohn Baldwin 	} else if (strcmp(name, "MaxOutstandingR2T") == 0) {
657839d0755SJohn Baldwin 		keys_add(response_keys, name, "1");
658839d0755SJohn Baldwin 	} else if (strcmp(name, "DataPDUInOrder") == 0) {
659839d0755SJohn Baldwin 		keys_add(response_keys, name, "Yes");
660839d0755SJohn Baldwin 	} else if (strcmp(name, "DataSequenceInOrder") == 0) {
661839d0755SJohn Baldwin 		keys_add(response_keys, name, "Yes");
662839d0755SJohn Baldwin 	} else if (strcmp(name, "ErrorRecoveryLevel") == 0) {
663839d0755SJohn Baldwin 		keys_add(response_keys, name, "0");
664839d0755SJohn Baldwin 	} else if (strcmp(name, "OFMarker") == 0) {
665839d0755SJohn Baldwin 		keys_add(response_keys, name, "No");
666839d0755SJohn Baldwin 	} else if (strcmp(name, "IFMarker") == 0) {
667839d0755SJohn Baldwin 		keys_add(response_keys, name, "No");
668839d0755SJohn Baldwin 	} else if (strcmp(name, "iSCSIProtocolLevel") == 0) {
669839d0755SJohn Baldwin 		tmp = strtoul(value, NULL, 10);
670839d0755SJohn Baldwin 		if (tmp > 2)
671839d0755SJohn Baldwin 			tmp = 2;
672839d0755SJohn Baldwin 		keys_add_int(response_keys, name, tmp);
673839d0755SJohn Baldwin 	} else {
674839d0755SJohn Baldwin 		log_debugx("unknown key \"%s\"; responding "
675839d0755SJohn Baldwin 		    "with NotUnderstood", name);
676839d0755SJohn Baldwin 		keys_add(response_keys, name, "NotUnderstood");
677839d0755SJohn Baldwin 	}
678839d0755SJohn Baldwin }
679839d0755SJohn Baldwin 
680839d0755SJohn Baldwin static void
login_redirect(struct pdu * request,const char * target_address)681839d0755SJohn Baldwin login_redirect(struct pdu *request, const char *target_address)
682839d0755SJohn Baldwin {
683839d0755SJohn Baldwin 	struct pdu *response;
684839d0755SJohn Baldwin 	struct iscsi_bhs_login_response *bhslr2;
685839d0755SJohn Baldwin 	struct keys *response_keys;
686839d0755SJohn Baldwin 
687839d0755SJohn Baldwin 	response = login_new_response(request);
688839d0755SJohn Baldwin 	login_set_csg(response, login_csg(request));
689839d0755SJohn Baldwin 	bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
690839d0755SJohn Baldwin 	bhslr2->bhslr_status_class = 0x01;
691839d0755SJohn Baldwin 	bhslr2->bhslr_status_detail = 0x01;
692839d0755SJohn Baldwin 
693839d0755SJohn Baldwin 	response_keys = keys_new();
694839d0755SJohn Baldwin 	keys_add(response_keys, "TargetAddress", target_address);
695839d0755SJohn Baldwin 
696839d0755SJohn Baldwin 	keys_save_pdu(response_keys, response);
697839d0755SJohn Baldwin 	pdu_send(response);
698839d0755SJohn Baldwin 	pdu_delete(response);
699839d0755SJohn Baldwin 	keys_delete(response_keys);
700839d0755SJohn Baldwin }
701839d0755SJohn Baldwin 
702839d0755SJohn Baldwin static bool
login_portal_redirect(struct ctld_connection * conn,struct pdu * request)703839d0755SJohn Baldwin login_portal_redirect(struct ctld_connection *conn, struct pdu *request)
704839d0755SJohn Baldwin {
705839d0755SJohn Baldwin 	const struct portal_group *pg;
706839d0755SJohn Baldwin 
707839d0755SJohn Baldwin 	pg = conn->conn_portal->p_portal_group;
708839d0755SJohn Baldwin 	if (pg->pg_redirection == NULL)
709839d0755SJohn Baldwin 		return (false);
710839d0755SJohn Baldwin 
711839d0755SJohn Baldwin 	log_debugx("portal-group \"%s\" configured to redirect to %s",
712839d0755SJohn Baldwin 	    pg->pg_name, pg->pg_redirection);
713839d0755SJohn Baldwin 	login_redirect(request, pg->pg_redirection);
714839d0755SJohn Baldwin 
715839d0755SJohn Baldwin 	return (true);
716839d0755SJohn Baldwin }
717839d0755SJohn Baldwin 
718839d0755SJohn Baldwin static bool
login_target_redirect(struct ctld_connection * conn,struct pdu * request)719839d0755SJohn Baldwin login_target_redirect(struct ctld_connection *conn, struct pdu *request)
720839d0755SJohn Baldwin {
721839d0755SJohn Baldwin 	const char *target_address;
722839d0755SJohn Baldwin 
723839d0755SJohn Baldwin 	assert(conn->conn_portal->p_portal_group->pg_redirection == NULL);
724839d0755SJohn Baldwin 
725839d0755SJohn Baldwin 	if (conn->conn_target == NULL)
726839d0755SJohn Baldwin 		return (false);
727839d0755SJohn Baldwin 
728839d0755SJohn Baldwin 	target_address = conn->conn_target->t_redirection;
729839d0755SJohn Baldwin 	if (target_address == NULL)
730839d0755SJohn Baldwin 		return (false);
731839d0755SJohn Baldwin 
732839d0755SJohn Baldwin 	log_debugx("target \"%s\" configured to redirect to %s",
733839d0755SJohn Baldwin 	  conn->conn_target->t_name, target_address);
734839d0755SJohn Baldwin 	login_redirect(request, target_address);
735839d0755SJohn Baldwin 
736839d0755SJohn Baldwin 	return (true);
737839d0755SJohn Baldwin }
738839d0755SJohn Baldwin 
739839d0755SJohn Baldwin static void
login_negotiate(struct ctld_connection * conn,struct pdu * request)740839d0755SJohn Baldwin login_negotiate(struct ctld_connection *conn, struct pdu *request)
741839d0755SJohn Baldwin {
742839d0755SJohn Baldwin 	struct pdu *response;
743839d0755SJohn Baldwin 	struct iscsi_bhs_login_response *bhslr2;
744839d0755SJohn Baldwin 	struct keys *request_keys, *response_keys;
745839d0755SJohn Baldwin 	int i;
746839d0755SJohn Baldwin 	bool redirected, skipped_security;
747839d0755SJohn Baldwin 
748839d0755SJohn Baldwin 	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
749839d0755SJohn Baldwin 		/*
750839d0755SJohn Baldwin 		 * Query the kernel for various size limits.  In case of
751839d0755SJohn Baldwin 		 * offload, it depends on hardware capabilities.
752839d0755SJohn Baldwin 		 */
753839d0755SJohn Baldwin 		assert(conn->conn_target != NULL);
754839d0755SJohn Baldwin 		conn->conn_max_recv_data_segment_limit = (1 << 24) - 1;
755839d0755SJohn Baldwin 		conn->conn_max_send_data_segment_limit = (1 << 24) - 1;
756839d0755SJohn Baldwin 		conn->conn_max_burst_limit = (1 << 24) - 1;
757839d0755SJohn Baldwin 		conn->conn_first_burst_limit = (1 << 24) - 1;
758839d0755SJohn Baldwin 		kernel_limits(conn->conn_portal->p_portal_group->pg_offload,
759839d0755SJohn Baldwin 		    conn->conn.conn_socket,
760839d0755SJohn Baldwin 		    &conn->conn_max_recv_data_segment_limit,
761839d0755SJohn Baldwin 		    &conn->conn_max_send_data_segment_limit,
762839d0755SJohn Baldwin 		    &conn->conn_max_burst_limit,
763839d0755SJohn Baldwin 		    &conn->conn_first_burst_limit);
764839d0755SJohn Baldwin 
765839d0755SJohn Baldwin 		/* We expect legal, usable values at this point. */
766839d0755SJohn Baldwin 		assert(conn->conn_max_recv_data_segment_limit >= 512);
767839d0755SJohn Baldwin 		assert(conn->conn_max_recv_data_segment_limit < (1 << 24));
768839d0755SJohn Baldwin 		assert(conn->conn_max_send_data_segment_limit >= 512);
769839d0755SJohn Baldwin 		assert(conn->conn_max_send_data_segment_limit < (1 << 24));
770839d0755SJohn Baldwin 		assert(conn->conn_max_burst_limit >= 512);
771839d0755SJohn Baldwin 		assert(conn->conn_max_burst_limit < (1 << 24));
772839d0755SJohn Baldwin 		assert(conn->conn_first_burst_limit >= 512);
773839d0755SJohn Baldwin 		assert(conn->conn_first_burst_limit < (1 << 24));
774839d0755SJohn Baldwin 		assert(conn->conn_first_burst_limit <=
775839d0755SJohn Baldwin 		    conn->conn_max_burst_limit);
776839d0755SJohn Baldwin 
777839d0755SJohn Baldwin 		/*
778839d0755SJohn Baldwin 		 * Limit default send length in case it won't be negotiated.
779839d0755SJohn Baldwin 		 * We can't do it for other limits, since they may affect both
780839d0755SJohn Baldwin 		 * sender and receiver operation, and we must obey defaults.
781839d0755SJohn Baldwin 		 */
782839d0755SJohn Baldwin 		if (conn->conn_max_send_data_segment_limit <
783839d0755SJohn Baldwin 		    conn->conn.conn_max_send_data_segment_length) {
784839d0755SJohn Baldwin 			conn->conn.conn_max_send_data_segment_length =
785839d0755SJohn Baldwin 			    conn->conn_max_send_data_segment_limit;
786839d0755SJohn Baldwin 		}
787839d0755SJohn Baldwin 	} else {
788839d0755SJohn Baldwin 		conn->conn_max_recv_data_segment_limit =
789839d0755SJohn Baldwin 		    MAX_DATA_SEGMENT_LENGTH;
790839d0755SJohn Baldwin 		conn->conn_max_send_data_segment_limit =
791839d0755SJohn Baldwin 		    MAX_DATA_SEGMENT_LENGTH;
792839d0755SJohn Baldwin 	}
793839d0755SJohn Baldwin 
794839d0755SJohn Baldwin 	if (request == NULL) {
795839d0755SJohn Baldwin 		log_debugx("beginning operational parameter negotiation; "
796839d0755SJohn Baldwin 		    "waiting for Login PDU");
797839d0755SJohn Baldwin 		request = login_receive(&conn->conn, false);
798839d0755SJohn Baldwin 		skipped_security = false;
799839d0755SJohn Baldwin 	} else
800839d0755SJohn Baldwin 		skipped_security = true;
801839d0755SJohn Baldwin 
802839d0755SJohn Baldwin 	/*
803839d0755SJohn Baldwin 	 * RFC 3720, 10.13.5.  Status-Class and Status-Detail, says
804839d0755SJohn Baldwin 	 * the redirection SHOULD be accepted by the initiator before
805839d0755SJohn Baldwin 	 * authentication, but MUST be accepted afterwards; that's
806839d0755SJohn Baldwin 	 * why we're doing it here and not earlier.
807839d0755SJohn Baldwin 	 */
808839d0755SJohn Baldwin 	redirected = login_target_redirect(conn, request);
809839d0755SJohn Baldwin 	if (redirected) {
810839d0755SJohn Baldwin 		log_debugx("initiator redirected; exiting");
811839d0755SJohn Baldwin 		exit(0);
812839d0755SJohn Baldwin 	}
813839d0755SJohn Baldwin 
814839d0755SJohn Baldwin 	request_keys = keys_new();
815839d0755SJohn Baldwin 	keys_load_pdu(request_keys, request);
816839d0755SJohn Baldwin 
817839d0755SJohn Baldwin 	response = login_new_response(request);
818839d0755SJohn Baldwin 	bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
819839d0755SJohn Baldwin 	bhslr2->bhslr_tsih = htons(0xbadd);
820839d0755SJohn Baldwin 	login_set_csg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
821839d0755SJohn Baldwin 	login_set_nsg(response, BHSLR_STAGE_FULL_FEATURE_PHASE);
822839d0755SJohn Baldwin 	response_keys = keys_new();
823839d0755SJohn Baldwin 
824839d0755SJohn Baldwin 	if (skipped_security &&
825839d0755SJohn Baldwin 	    conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
826839d0755SJohn Baldwin 		if (conn->conn_target->t_alias != NULL)
827839d0755SJohn Baldwin 			keys_add(response_keys,
828839d0755SJohn Baldwin 			    "TargetAlias", conn->conn_target->t_alias);
829839d0755SJohn Baldwin 		keys_add_int(response_keys, "TargetPortalGroupTag",
830839d0755SJohn Baldwin 		    conn->conn_portal->p_portal_group->pg_tag);
831839d0755SJohn Baldwin 	}
832839d0755SJohn Baldwin 
833839d0755SJohn Baldwin 	for (i = 0; i < KEYS_MAX; i++) {
834839d0755SJohn Baldwin 		if (request_keys->keys_names[i] == NULL)
835839d0755SJohn Baldwin 			break;
836839d0755SJohn Baldwin 
837839d0755SJohn Baldwin 		login_negotiate_key(request, request_keys->keys_names[i],
838839d0755SJohn Baldwin 		    request_keys->keys_values[i], skipped_security,
839839d0755SJohn Baldwin 		    response_keys);
840839d0755SJohn Baldwin 	}
841839d0755SJohn Baldwin 
842839d0755SJohn Baldwin 	/*
843839d0755SJohn Baldwin 	 * We'd started with usable values at our end.  But a bad initiator
844839d0755SJohn Baldwin 	 * could have presented a large FirstBurstLength and then a smaller
845839d0755SJohn Baldwin 	 * MaxBurstLength (in that order) and because we process the key/value
846839d0755SJohn Baldwin 	 * pairs in the order they are in the request we might have ended up
847839d0755SJohn Baldwin 	 * with illegal values here.
848839d0755SJohn Baldwin 	 */
849839d0755SJohn Baldwin 	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL &&
850839d0755SJohn Baldwin 	    conn->conn.conn_first_burst_length >
851839d0755SJohn Baldwin 	    conn->conn.conn_max_burst_length) {
852839d0755SJohn Baldwin 		log_errx(1, "initiator sent FirstBurstLength > MaxBurstLength");
853839d0755SJohn Baldwin 	}
854839d0755SJohn Baldwin 
855839d0755SJohn Baldwin 	conn->conn.conn_max_recv_data_segment_length =
856839d0755SJohn Baldwin 	    conn->conn_max_recv_data_segment_limit;
857839d0755SJohn Baldwin 	keys_add_int(response_keys, "MaxRecvDataSegmentLength",
858839d0755SJohn Baldwin 		    conn->conn.conn_max_recv_data_segment_length);
859839d0755SJohn Baldwin 
860839d0755SJohn Baldwin 	log_debugx("operational parameter negotiation done; "
861839d0755SJohn Baldwin 	    "transitioning to Full Feature Phase");
862839d0755SJohn Baldwin 
863839d0755SJohn Baldwin 	keys_save_pdu(response_keys, response);
864839d0755SJohn Baldwin 	pdu_send(response);
865839d0755SJohn Baldwin 	pdu_delete(response);
866839d0755SJohn Baldwin 	keys_delete(response_keys);
867839d0755SJohn Baldwin 	pdu_delete(request);
868839d0755SJohn Baldwin 	keys_delete(request_keys);
869839d0755SJohn Baldwin }
870839d0755SJohn Baldwin 
871839d0755SJohn Baldwin static void
login_wait_transition(struct ctld_connection * conn)872839d0755SJohn Baldwin login_wait_transition(struct ctld_connection *conn)
873839d0755SJohn Baldwin {
874839d0755SJohn Baldwin 	struct pdu *request, *response;
875839d0755SJohn Baldwin 	struct iscsi_bhs_login_request *bhslr;
876839d0755SJohn Baldwin 
877839d0755SJohn Baldwin 	log_debugx("waiting for state transition request");
878839d0755SJohn Baldwin 	request = login_receive(&conn->conn, false);
879839d0755SJohn Baldwin 	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
880839d0755SJohn Baldwin 	if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) {
881839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x00);
882839d0755SJohn Baldwin 		log_errx(1, "got no \"T\" flag after answering AuthMethod");
883839d0755SJohn Baldwin 	}
884839d0755SJohn Baldwin 
885839d0755SJohn Baldwin 	log_debugx("got state transition request");
886839d0755SJohn Baldwin 	response = login_new_response(request);
887839d0755SJohn Baldwin 	pdu_delete(request);
888839d0755SJohn Baldwin 	login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
889839d0755SJohn Baldwin 	pdu_send(response);
890839d0755SJohn Baldwin 	pdu_delete(response);
891839d0755SJohn Baldwin 
892839d0755SJohn Baldwin 	login_negotiate(conn, NULL);
893839d0755SJohn Baldwin }
894839d0755SJohn Baldwin 
895839d0755SJohn Baldwin void
login(struct ctld_connection * conn)896839d0755SJohn Baldwin login(struct ctld_connection *conn)
897839d0755SJohn Baldwin {
898839d0755SJohn Baldwin 	struct pdu *request, *response;
899839d0755SJohn Baldwin 	struct iscsi_bhs_login_request *bhslr;
900839d0755SJohn Baldwin 	struct keys *request_keys, *response_keys;
901839d0755SJohn Baldwin 	struct auth_group *ag;
902839d0755SJohn Baldwin 	struct portal_group *pg;
903839d0755SJohn Baldwin 	const char *initiator_name, *initiator_alias, *session_type,
904839d0755SJohn Baldwin 	    *target_name, *auth_method;
905839d0755SJohn Baldwin 	bool redirected, fail, trans;
906839d0755SJohn Baldwin 
907839d0755SJohn Baldwin 	/*
908839d0755SJohn Baldwin 	 * Handle the initial Login Request - figure out required authentication
909839d0755SJohn Baldwin 	 * method and either transition to the next phase, if no authentication
910839d0755SJohn Baldwin 	 * is required, or call appropriate authentication code.
911839d0755SJohn Baldwin 	 */
912839d0755SJohn Baldwin 	log_debugx("beginning Login Phase; waiting for Login PDU");
913839d0755SJohn Baldwin 	request = login_receive(&conn->conn, true);
914839d0755SJohn Baldwin 	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
915839d0755SJohn Baldwin 	if (bhslr->bhslr_tsih != 0) {
916839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x0a);
917839d0755SJohn Baldwin 		log_errx(1, "received Login PDU with non-zero TSIH");
918839d0755SJohn Baldwin 	}
919839d0755SJohn Baldwin 
920839d0755SJohn Baldwin 	pg = conn->conn_portal->p_portal_group;
921839d0755SJohn Baldwin 
922839d0755SJohn Baldwin 	memcpy(conn->conn_initiator_isid, bhslr->bhslr_isid,
923839d0755SJohn Baldwin 	    sizeof(conn->conn_initiator_isid));
924839d0755SJohn Baldwin 
925839d0755SJohn Baldwin 	/*
926839d0755SJohn Baldwin 	 * XXX: Implement the C flag some day.
927839d0755SJohn Baldwin 	 */
928839d0755SJohn Baldwin 	request_keys = keys_new();
929839d0755SJohn Baldwin 	keys_load_pdu(request_keys, request);
930839d0755SJohn Baldwin 
931839d0755SJohn Baldwin 	assert(conn->conn_initiator_name == NULL);
932839d0755SJohn Baldwin 	initiator_name = keys_find(request_keys, "InitiatorName");
933839d0755SJohn Baldwin 	if (initiator_name == NULL) {
934839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x07);
935839d0755SJohn Baldwin 		log_errx(1, "received Login PDU without InitiatorName");
936839d0755SJohn Baldwin 	}
937839d0755SJohn Baldwin 	if (valid_iscsi_name(initiator_name, log_warnx) == false) {
938839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x00);
939839d0755SJohn Baldwin 		log_errx(1, "received Login PDU with invalid InitiatorName");
940839d0755SJohn Baldwin 	}
941839d0755SJohn Baldwin 	conn->conn_initiator_name = checked_strdup(initiator_name);
942839d0755SJohn Baldwin 	log_set_peer_name(conn->conn_initiator_name);
943839d0755SJohn Baldwin 	setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name);
944839d0755SJohn Baldwin 
945839d0755SJohn Baldwin 	redirected = login_portal_redirect(conn, request);
946839d0755SJohn Baldwin 	if (redirected) {
947839d0755SJohn Baldwin 		log_debugx("initiator redirected; exiting");
948839d0755SJohn Baldwin 		exit(0);
949839d0755SJohn Baldwin 	}
950839d0755SJohn Baldwin 
951839d0755SJohn Baldwin 	initiator_alias = keys_find(request_keys, "InitiatorAlias");
952839d0755SJohn Baldwin 	if (initiator_alias != NULL)
953839d0755SJohn Baldwin 		conn->conn_initiator_alias = checked_strdup(initiator_alias);
954839d0755SJohn Baldwin 
955839d0755SJohn Baldwin 	assert(conn->conn_session_type == CONN_SESSION_TYPE_NONE);
956839d0755SJohn Baldwin 	session_type = keys_find(request_keys, "SessionType");
957839d0755SJohn Baldwin 	if (session_type != NULL) {
958839d0755SJohn Baldwin 		if (strcmp(session_type, "Normal") == 0) {
959839d0755SJohn Baldwin 			conn->conn_session_type = CONN_SESSION_TYPE_NORMAL;
960839d0755SJohn Baldwin 		} else if (strcmp(session_type, "Discovery") == 0) {
961839d0755SJohn Baldwin 			conn->conn_session_type = CONN_SESSION_TYPE_DISCOVERY;
962839d0755SJohn Baldwin 		} else {
963839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x00);
964839d0755SJohn Baldwin 			log_errx(1, "received Login PDU with invalid "
965839d0755SJohn Baldwin 			    "SessionType \"%s\"", session_type);
966839d0755SJohn Baldwin 		}
967839d0755SJohn Baldwin 	} else
968839d0755SJohn Baldwin 		conn->conn_session_type = CONN_SESSION_TYPE_NORMAL;
969839d0755SJohn Baldwin 
970839d0755SJohn Baldwin 	assert(conn->conn_target == NULL);
971839d0755SJohn Baldwin 	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
972839d0755SJohn Baldwin 		target_name = keys_find(request_keys, "TargetName");
973839d0755SJohn Baldwin 		if (target_name == NULL) {
974839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x07);
975839d0755SJohn Baldwin 			log_errx(1, "received Login PDU without TargetName");
976839d0755SJohn Baldwin 		}
977839d0755SJohn Baldwin 
978839d0755SJohn Baldwin 		conn->conn_port = port_find_in_pg(pg, target_name);
979839d0755SJohn Baldwin 		if (conn->conn_port == NULL) {
980839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x03);
981839d0755SJohn Baldwin 			log_errx(1, "requested target \"%s\" not found",
982839d0755SJohn Baldwin 			    target_name);
983839d0755SJohn Baldwin 		}
984839d0755SJohn Baldwin 		conn->conn_target = conn->conn_port->p_target;
985839d0755SJohn Baldwin 	}
986839d0755SJohn Baldwin 
987839d0755SJohn Baldwin 	/*
988839d0755SJohn Baldwin 	 * At this point we know what kind of authentication we need.
989839d0755SJohn Baldwin 	 */
990839d0755SJohn Baldwin 	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
991839d0755SJohn Baldwin 		ag = conn->conn_port->p_auth_group;
992839d0755SJohn Baldwin 		if (ag == NULL)
993839d0755SJohn Baldwin 			ag = conn->conn_target->t_auth_group;
994839d0755SJohn Baldwin 		if (ag->ag_name != NULL) {
995839d0755SJohn Baldwin 			log_debugx("initiator requests to connect "
996839d0755SJohn Baldwin 			    "to target \"%s\"; auth-group \"%s\"",
997839d0755SJohn Baldwin 			    conn->conn_target->t_name,
998839d0755SJohn Baldwin 			    ag->ag_name);
999839d0755SJohn Baldwin 		} else {
1000839d0755SJohn Baldwin 			log_debugx("initiator requests to connect "
1001839d0755SJohn Baldwin 			    "to target \"%s\"", conn->conn_target->t_name);
1002839d0755SJohn Baldwin 		}
1003839d0755SJohn Baldwin 	} else {
1004839d0755SJohn Baldwin 		assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY);
1005839d0755SJohn Baldwin 		ag = pg->pg_discovery_auth_group;
1006*ce8cfd99SJohn Baldwin 		log_debugx("initiator requests discovery session; %s",
1007*ce8cfd99SJohn Baldwin 		    ag->ag_label);
1008839d0755SJohn Baldwin 	}
1009839d0755SJohn Baldwin 
1010839d0755SJohn Baldwin 	if (ag->ag_type == AG_TYPE_DENY) {
1011839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x01);
1012839d0755SJohn Baldwin 		log_errx(1, "auth-type is \"deny\"");
1013839d0755SJohn Baldwin 	}
1014839d0755SJohn Baldwin 
1015839d0755SJohn Baldwin 	if (ag->ag_type == AG_TYPE_UNKNOWN) {
1016839d0755SJohn Baldwin 		/*
1017839d0755SJohn Baldwin 		 * This can happen with empty auth-group.
1018839d0755SJohn Baldwin 		 */
1019839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x01);
1020839d0755SJohn Baldwin 		log_errx(1, "auth-type not set, denying access");
1021839d0755SJohn Baldwin 	}
1022839d0755SJohn Baldwin 
1023839d0755SJohn Baldwin 	/*
1024839d0755SJohn Baldwin 	 * Enforce initiator-name and initiator-portal.
1025839d0755SJohn Baldwin 	 */
1026839d0755SJohn Baldwin 	if (!auth_name_check(ag, initiator_name)) {
1027839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x02);
1028839d0755SJohn Baldwin 		log_errx(1, "initiator does not match allowed initiator names");
1029839d0755SJohn Baldwin 	}
1030839d0755SJohn Baldwin 
1031839d0755SJohn Baldwin 	if (!auth_portal_check(ag, &conn->conn_initiator_sa)) {
1032839d0755SJohn Baldwin 		login_send_error(request, 0x02, 0x02);
1033839d0755SJohn Baldwin 		log_errx(1, "initiator does not match allowed "
1034839d0755SJohn Baldwin 		    "initiator portals");
1035839d0755SJohn Baldwin 	}
1036839d0755SJohn Baldwin 
1037839d0755SJohn Baldwin 	/*
1038839d0755SJohn Baldwin 	 * Let's see if the initiator intends to do any kind of authentication
1039839d0755SJohn Baldwin 	 * at all.
1040839d0755SJohn Baldwin 	 */
1041839d0755SJohn Baldwin 	if (login_csg(request) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) {
1042839d0755SJohn Baldwin 		if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) {
1043839d0755SJohn Baldwin 			login_send_error(request, 0x02, 0x01);
1044839d0755SJohn Baldwin 			log_errx(1, "initiator skipped the authentication, "
1045839d0755SJohn Baldwin 			    "but authentication is required");
1046839d0755SJohn Baldwin 		}
1047839d0755SJohn Baldwin 
1048839d0755SJohn Baldwin 		keys_delete(request_keys);
1049839d0755SJohn Baldwin 
1050839d0755SJohn Baldwin 		log_debugx("initiator skipped the authentication, "
1051839d0755SJohn Baldwin 		    "and we don't need it; proceeding with negotiation");
1052839d0755SJohn Baldwin 		login_negotiate(conn, request);
1053839d0755SJohn Baldwin 		return;
1054839d0755SJohn Baldwin 	}
1055839d0755SJohn Baldwin 
1056839d0755SJohn Baldwin 	fail = false;
1057839d0755SJohn Baldwin 	response = login_new_response(request);
1058839d0755SJohn Baldwin 	response_keys = keys_new();
1059839d0755SJohn Baldwin 	trans = (bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0;
1060839d0755SJohn Baldwin 	auth_method = keys_find(request_keys, "AuthMethod");
1061839d0755SJohn Baldwin 	if (ag->ag_type == AG_TYPE_NO_AUTHENTICATION) {
1062839d0755SJohn Baldwin 		log_debugx("authentication not required");
1063839d0755SJohn Baldwin 		if (auth_method == NULL ||
1064839d0755SJohn Baldwin 		    login_list_contains(auth_method, "None")) {
1065839d0755SJohn Baldwin 			keys_add(response_keys, "AuthMethod", "None");
1066839d0755SJohn Baldwin 		} else {
1067839d0755SJohn Baldwin 			log_warnx("initiator requests "
1068839d0755SJohn Baldwin 			    "AuthMethod \"%s\" instead of \"None\"",
1069839d0755SJohn Baldwin 			    auth_method);
1070839d0755SJohn Baldwin 			keys_add(response_keys, "AuthMethod", "Reject");
1071839d0755SJohn Baldwin 		}
1072839d0755SJohn Baldwin 		if (trans)
1073839d0755SJohn Baldwin 			login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
1074839d0755SJohn Baldwin 	} else {
1075839d0755SJohn Baldwin 		log_debugx("CHAP authentication required");
1076839d0755SJohn Baldwin 		if (auth_method == NULL ||
1077839d0755SJohn Baldwin 		    login_list_contains(auth_method, "CHAP")) {
1078839d0755SJohn Baldwin 			keys_add(response_keys, "AuthMethod", "CHAP");
1079839d0755SJohn Baldwin 		} else {
1080839d0755SJohn Baldwin 			log_warnx("initiator requests unsupported "
1081839d0755SJohn Baldwin 			    "AuthMethod \"%s\" instead of \"CHAP\"",
1082839d0755SJohn Baldwin 			    auth_method);
1083839d0755SJohn Baldwin 			keys_add(response_keys, "AuthMethod", "Reject");
1084839d0755SJohn Baldwin 			fail = true;
1085839d0755SJohn Baldwin 		}
1086839d0755SJohn Baldwin 	}
1087839d0755SJohn Baldwin 	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
1088839d0755SJohn Baldwin 		if (conn->conn_target->t_alias != NULL)
1089839d0755SJohn Baldwin 			keys_add(response_keys,
1090839d0755SJohn Baldwin 			    "TargetAlias", conn->conn_target->t_alias);
1091839d0755SJohn Baldwin 		keys_add_int(response_keys,
1092839d0755SJohn Baldwin 		    "TargetPortalGroupTag", pg->pg_tag);
1093839d0755SJohn Baldwin 	}
1094839d0755SJohn Baldwin 	keys_save_pdu(response_keys, response);
1095839d0755SJohn Baldwin 
1096839d0755SJohn Baldwin 	pdu_send(response);
1097839d0755SJohn Baldwin 	pdu_delete(response);
1098839d0755SJohn Baldwin 	keys_delete(response_keys);
1099839d0755SJohn Baldwin 	pdu_delete(request);
1100839d0755SJohn Baldwin 	keys_delete(request_keys);
1101839d0755SJohn Baldwin 
1102839d0755SJohn Baldwin 	if (fail) {
1103839d0755SJohn Baldwin 		log_debugx("sent reject for AuthMethod; exiting");
1104839d0755SJohn Baldwin 		exit(1);
1105839d0755SJohn Baldwin 	}
1106839d0755SJohn Baldwin 
1107839d0755SJohn Baldwin 	if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) {
1108839d0755SJohn Baldwin 		login_chap(conn, ag);
1109839d0755SJohn Baldwin 		login_negotiate(conn, NULL);
1110839d0755SJohn Baldwin 	} else if (trans) {
1111839d0755SJohn Baldwin 		login_negotiate(conn, NULL);
1112839d0755SJohn Baldwin 	} else {
1113839d0755SJohn Baldwin 		login_wait_transition(conn);
1114839d0755SJohn Baldwin 	}
1115839d0755SJohn Baldwin }
1116