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/types.h>
33#include <sys/ioctl.h>
34#include <assert.h>
35#include <stdbool.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <netinet/in.h>
40
41#include "iscsid.h"
42#include "iscsi_proto.h"
43
44static int
45login_nsg(const struct pdu *response)
46{
47	struct iscsi_bhs_login_response *bhslr;
48
49	bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
50
51	return (bhslr->bhslr_flags & 0x03);
52}
53
54static void
55login_set_nsg(struct pdu *request, int nsg)
56{
57	struct iscsi_bhs_login_request *bhslr;
58
59	assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
60	    nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
61	    nsg == BHSLR_STAGE_FULL_FEATURE_PHASE);
62
63	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
64
65	bhslr->bhslr_flags &= 0xFC;
66	bhslr->bhslr_flags |= nsg;
67}
68
69static void
70login_set_csg(struct pdu *request, int csg)
71{
72	struct iscsi_bhs_login_request *bhslr;
73
74	assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
75	    csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
76	    csg == BHSLR_STAGE_FULL_FEATURE_PHASE);
77
78	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
79
80	bhslr->bhslr_flags &= 0xF3;
81	bhslr->bhslr_flags |= csg << 2;
82}
83
84static const char *
85login_target_error_str(int class, int detail)
86{
87	static char msg[128];
88
89	/*
90	 * RFC 3270, 10.13.5.  Status-Class and Status-Detail
91	 */
92	switch (class) {
93	case 0x01:
94		switch (detail) {
95		case 0x01:
96			return ("Target moved temporarily");
97		case 0x02:
98			return ("Target moved permanently");
99		default:
100			snprintf(msg, sizeof(msg), "unknown redirection; "
101			    "Status-Class 0x%x, Status-Detail 0x%x",
102			    class, detail);
103			return (msg);
104		}
105	case 0x02:
106		switch (detail) {
107		case 0x00:
108			return ("Initiator error");
109		case 0x01:
110			return ("Authentication failure");
111		case 0x02:
112			return ("Authorization failure");
113		case 0x03:
114			return ("Not found");
115		case 0x04:
116			return ("Target removed");
117		case 0x05:
118			return ("Unsupported version");
119		case 0x06:
120			return ("Too many connections");
121		case 0x07:
122			return ("Missing parameter");
123		case 0x08:
124			return ("Can't include in session");
125		case 0x09:
126			return ("Session type not supported");
127		case 0x0a:
128			return ("Session does not exist");
129		case 0x0b:
130			return ("Invalid during login");
131		default:
132			snprintf(msg, sizeof(msg), "unknown initiator error; "
133			    "Status-Class 0x%x, Status-Detail 0x%x",
134			    class, detail);
135			return (msg);
136		}
137	case 0x03:
138		switch (detail) {
139		case 0x00:
140			return ("Target error");
141		case 0x01:
142			return ("Service unavailable");
143		case 0x02:
144			return ("Out of resources");
145		default:
146			snprintf(msg, sizeof(msg), "unknown target error; "
147			    "Status-Class 0x%x, Status-Detail 0x%x",
148			    class, detail);
149			return (msg);
150		}
151	default:
152		snprintf(msg, sizeof(msg), "unknown error; "
153		    "Status-Class 0x%x, Status-Detail 0x%x",
154		    class, detail);
155		return (msg);
156	}
157}
158
159static void
160kernel_modify(const struct iscsid_connection *conn, const char *target_address)
161{
162	struct iscsi_session_modify ism;
163	int error;
164
165	memset(&ism, 0, sizeof(ism));
166	ism.ism_session_id = conn->conn_session_id;
167	memcpy(&ism.ism_conf, &conn->conn_conf, sizeof(ism.ism_conf));
168	strlcpy(ism.ism_conf.isc_target_addr, target_address,
169	    sizeof(ism.ism_conf.isc_target_addr));
170	error = ioctl(conn->conn_iscsi_fd, ISCSISMODIFY, &ism);
171	if (error != 0) {
172		log_err(1, "failed to redirect to %s: ISCSISMODIFY",
173		    target_address);
174	}
175}
176
177/*
178 * XXX:	The way it works is suboptimal; what should happen is described
179 *	in draft-gilligan-iscsi-fault-tolerance-00.  That, however, would
180 *	be much more complicated: we would need to keep "dependencies"
181 *	for sessions, so that, in case described in draft and using draft
182 *	terminology, we would have three sessions: one for discovery,
183 *	one for initial target portal, and one for redirect portal.
184 *	This would allow us to "backtrack" on connection failure,
185 *	as described in draft.
186 */
187static void
188login_handle_redirection(struct iscsid_connection *conn, struct pdu *response)
189{
190	struct iscsi_bhs_login_response *bhslr;
191	struct keys *response_keys;
192	const char *target_address;
193
194	bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
195	assert (bhslr->bhslr_status_class == 1);
196
197	response_keys = keys_new();
198	keys_load_pdu(response_keys, response);
199
200	target_address = keys_find(response_keys, "TargetAddress");
201	if (target_address == NULL)
202		log_errx(1, "received redirection without TargetAddress");
203	if (target_address[0] == '\0')
204		log_errx(1, "received redirection with empty TargetAddress");
205	if (strlen(target_address) >=
206	    sizeof(conn->conn_conf.isc_target_addr) - 1)
207		log_errx(1, "received TargetAddress is too long");
208
209	log_debugx("received redirection to \"%s\"", target_address);
210	kernel_modify(conn, target_address);
211	keys_delete(response_keys);
212}
213
214static struct pdu *
215login_receive(struct connection *conn)
216{
217	struct pdu *response;
218	struct iscsi_bhs_login_response *bhslr;
219	const char *errorstr;
220	static bool initial = true;
221
222	response = pdu_new(conn);
223	pdu_receive(response);
224	if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGIN_RESPONSE) {
225		log_errx(1, "protocol error: received invalid opcode 0x%x",
226		    response->pdu_bhs->bhs_opcode);
227	}
228	bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
229	/*
230	 * XXX: Implement the C flag some day.
231	 */
232	if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0)
233		log_errx(1, "received Login PDU with unsupported \"C\" flag");
234	if (bhslr->bhslr_version_max != 0x00)
235		log_errx(1, "received Login PDU with unsupported "
236		    "Version-max 0x%x", bhslr->bhslr_version_max);
237	if (bhslr->bhslr_version_active != 0x00)
238		log_errx(1, "received Login PDU with unsupported "
239		    "Version-active 0x%x", bhslr->bhslr_version_active);
240	if (bhslr->bhslr_status_class == 1) {
241		login_handle_redirection((struct iscsid_connection *)conn,
242		    response);
243		log_debugx("redirection handled; exiting");
244		exit(0);
245	}
246	if (bhslr->bhslr_status_class != 0) {
247		errorstr = login_target_error_str(bhslr->bhslr_status_class,
248		    bhslr->bhslr_status_detail);
249		fail(conn, errorstr);
250		log_errx(1, "target returned error: %s", errorstr);
251	}
252	if (initial == false &&
253	    ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) {
254		/*
255		 * It's a warning, not an error, to work around what seems
256		 * to be bug in NetBSD iSCSI target.
257		 */
258		log_warnx("received Login PDU with wrong StatSN: "
259		    "is %u, should be %u", ntohl(bhslr->bhslr_statsn),
260		    conn->conn_statsn + 1);
261	}
262	conn->conn_tsih = ntohs(bhslr->bhslr_tsih);
263	conn->conn_statsn = ntohl(bhslr->bhslr_statsn);
264
265	initial = false;
266
267	return (response);
268}
269
270static struct pdu *
271login_new_request(struct connection *conn, int csg)
272{
273	struct pdu *request;
274	struct iscsi_bhs_login_request *bhslr;
275	int nsg;
276
277	request = pdu_new(conn);
278	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
279	bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_REQUEST |
280	    ISCSI_BHS_OPCODE_IMMEDIATE;
281
282	bhslr->bhslr_flags = BHSLR_FLAGS_TRANSIT;
283	switch (csg) {
284	case BHSLR_STAGE_SECURITY_NEGOTIATION:
285		nsg = BHSLR_STAGE_OPERATIONAL_NEGOTIATION;
286		break;
287	case BHSLR_STAGE_OPERATIONAL_NEGOTIATION:
288		nsg = BHSLR_STAGE_FULL_FEATURE_PHASE;
289		break;
290	default:
291		assert(!"invalid csg");
292		log_errx(1, "invalid csg %d", csg);
293	}
294	login_set_csg(request, csg);
295	login_set_nsg(request, nsg);
296
297	memcpy(bhslr->bhslr_isid, &conn->conn_isid, sizeof(bhslr->bhslr_isid));
298	bhslr->bhslr_tsih = htons(conn->conn_tsih);
299	bhslr->bhslr_initiator_task_tag = 0;
300	bhslr->bhslr_cmdsn = 0;
301	bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1);
302
303	return (request);
304}
305
306static int
307login_list_prefers(const char *list,
308    const char *choice1, const char *choice2)
309{
310	char *tofree, *str, *token;
311
312	tofree = str = checked_strdup(list);
313
314	while ((token = strsep(&str, ",")) != NULL) {
315		if (strcmp(token, choice1) == 0) {
316			free(tofree);
317			return (1);
318		}
319		if (strcmp(token, choice2) == 0) {
320			free(tofree);
321			return (2);
322		}
323	}
324	free(tofree);
325	return (-1);
326}
327
328static void
329login_negotiate_key(struct iscsid_connection *conn, const char *name,
330    const char *value)
331{
332	struct iscsi_session_limits *isl;
333	int which, tmp;
334
335	isl = &conn->conn_limits;
336	if (strcmp(name, "TargetAlias") == 0) {
337		strlcpy(conn->conn_target_alias, value,
338		    sizeof(conn->conn_target_alias));
339	} else if (strcmp(value, "Irrelevant") == 0) {
340		/* Ignore. */
341	} else if (strcmp(name, "iSCSIProtocolLevel") == 0) {
342		tmp = strtoul(value, NULL, 10);
343		if (tmp < 0 || tmp > 31)
344			log_errx(1, "received invalid iSCSIProtocolLevel");
345		conn->conn_protocol_level = tmp;
346	} else if (strcmp(name, "HeaderDigest") == 0) {
347		which = login_list_prefers(value, "CRC32C", "None");
348		switch (which) {
349		case 1:
350			log_debugx("target prefers CRC32C "
351			    "for header digest; we'll use it");
352			conn->conn.conn_header_digest = CONN_DIGEST_CRC32C;
353			break;
354		case 2:
355			log_debugx("target prefers not to do "
356			    "header digest; we'll comply");
357			break;
358		default:
359			log_warnx("target sent unrecognized "
360			    "HeaderDigest value \"%s\"; will use None", value);
361			break;
362		}
363	} else if (strcmp(name, "DataDigest") == 0) {
364		which = login_list_prefers(value, "CRC32C", "None");
365		switch (which) {
366		case 1:
367			log_debugx("target prefers CRC32C "
368			    "for data digest; we'll use it");
369			conn->conn.conn_data_digest = CONN_DIGEST_CRC32C;
370			break;
371		case 2:
372			log_debugx("target prefers not to do "
373			    "data digest; we'll comply");
374			break;
375		default:
376			log_warnx("target sent unrecognized "
377			    "DataDigest value \"%s\"; will use None", value);
378			break;
379		}
380	} else if (strcmp(name, "MaxConnections") == 0) {
381		/* Ignore. */
382	} else if (strcmp(name, "InitialR2T") == 0) {
383		if (strcmp(value, "Yes") == 0)
384			conn->conn_initial_r2t = true;
385		else
386			conn->conn_initial_r2t = false;
387	} else if (strcmp(name, "ImmediateData") == 0) {
388		if (strcmp(value, "Yes") == 0)
389			conn->conn.conn_immediate_data = true;
390		else
391			conn->conn.conn_immediate_data = false;
392	} else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) {
393		tmp = strtoul(value, NULL, 10);
394		if (tmp <= 0)
395			log_errx(1, "received invalid "
396			    "MaxRecvDataSegmentLength");
397		if (tmp > isl->isl_max_send_data_segment_length) {
398			log_debugx("capping max_send_data_segment_length "
399			    "from %d to %d", tmp,
400			    isl->isl_max_send_data_segment_length);
401			tmp = isl->isl_max_send_data_segment_length;
402		}
403		conn->conn.conn_max_send_data_segment_length = tmp;
404	} else if (strcmp(name, "MaxBurstLength") == 0) {
405		tmp = strtoul(value, NULL, 10);
406		if (tmp <= 0)
407			log_errx(1, "received invalid MaxBurstLength");
408		if (tmp > isl->isl_max_burst_length) {
409			log_debugx("capping MaxBurstLength "
410			    "from %d to %d", tmp, isl->isl_max_burst_length);
411			tmp = isl->isl_max_burst_length;
412		}
413		conn->conn.conn_max_burst_length = tmp;
414	} else if (strcmp(name, "FirstBurstLength") == 0) {
415		tmp = strtoul(value, NULL, 10);
416		if (tmp <= 0)
417			log_errx(1, "received invalid FirstBurstLength");
418		if (tmp > isl->isl_first_burst_length) {
419			log_debugx("capping FirstBurstLength "
420			    "from %d to %d", tmp, isl->isl_first_burst_length);
421			tmp = isl->isl_first_burst_length;
422		}
423		conn->conn.conn_first_burst_length = tmp;
424	} else if (strcmp(name, "DefaultTime2Wait") == 0) {
425		/* Ignore */
426	} else if (strcmp(name, "DefaultTime2Retain") == 0) {
427		/* Ignore */
428	} else if (strcmp(name, "MaxOutstandingR2T") == 0) {
429		/* Ignore */
430	} else if (strcmp(name, "DataPDUInOrder") == 0) {
431		/* Ignore */
432	} else if (strcmp(name, "DataSequenceInOrder") == 0) {
433		/* Ignore */
434	} else if (strcmp(name, "ErrorRecoveryLevel") == 0) {
435		/* Ignore */
436	} else if (strcmp(name, "OFMarker") == 0) {
437		/* Ignore */
438	} else if (strcmp(name, "IFMarker") == 0) {
439		/* Ignore */
440	} else if (strcmp(name, "RDMAExtensions") == 0) {
441		if (conn->conn_conf.isc_iser == 1 &&
442		    strcmp(value, "Yes") != 0) {
443			log_errx(1, "received unsupported RDMAExtensions");
444		}
445	} else if (strcmp(name, "InitiatorRecvDataSegmentLength") == 0) {
446		tmp = strtoul(value, NULL, 10);
447		if (tmp <= 0)
448			log_errx(1, "received invalid "
449			    "InitiatorRecvDataSegmentLength");
450		if ((int)tmp > isl->isl_max_recv_data_segment_length) {
451			log_debugx("capping InitiatorRecvDataSegmentLength "
452			    "from %d to %d", tmp,
453			    isl->isl_max_recv_data_segment_length);
454			tmp = isl->isl_max_recv_data_segment_length;
455		}
456		conn->conn.conn_max_recv_data_segment_length = tmp;
457	} else if (strcmp(name, "TargetPortalGroupTag") == 0) {
458		/* Ignore */
459	} else if (strcmp(name, "TargetRecvDataSegmentLength") == 0) {
460		tmp = strtoul(value, NULL, 10);
461		if (tmp <= 0) {
462			log_errx(1,
463			    "received invalid TargetRecvDataSegmentLength");
464		}
465		if (tmp > isl->isl_max_send_data_segment_length) {
466			log_debugx("capping TargetRecvDataSegmentLength "
467			    "from %d to %d", tmp,
468			    isl->isl_max_send_data_segment_length);
469			tmp = isl->isl_max_send_data_segment_length;
470		}
471		conn->conn.conn_max_send_data_segment_length = tmp;
472	} else {
473		log_debugx("unknown key \"%s\"; ignoring",  name);
474	}
475}
476
477static void
478login_negotiate(struct iscsid_connection *conn)
479{
480	struct pdu *request, *response;
481	struct keys *request_keys, *response_keys;
482	struct iscsi_bhs_login_response *bhslr;
483	int i, nrequests = 0;
484	struct iscsi_session_limits *isl;
485
486	log_debugx("beginning operational parameter negotiation");
487	request = login_new_request(&conn->conn,
488	    BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
489	request_keys = keys_new();
490
491	isl = &conn->conn_limits;
492	log_debugx("Limits for offload \"%s\" are "
493	    "MaxRecvDataSegment=%d, max_send_dsl=%d, "
494	    "MaxBurstLength=%d, FirstBurstLength=%d",
495	    conn->conn_conf.isc_offload, isl->isl_max_recv_data_segment_length,
496	    isl->isl_max_send_data_segment_length, isl->isl_max_burst_length,
497	    isl->isl_first_burst_length);
498
499	/*
500	 * The following keys are irrelevant for discovery sessions.
501	 */
502	if (conn->conn_conf.isc_discovery == 0) {
503		keys_add(request_keys, "iSCSIProtocolLevel", "2");
504		if (conn->conn_conf.isc_header_digest != 0)
505			keys_add(request_keys, "HeaderDigest", "CRC32C");
506		else
507			keys_add(request_keys, "HeaderDigest", "None");
508		if (conn->conn_conf.isc_data_digest != 0)
509			keys_add(request_keys, "DataDigest", "CRC32C");
510		else
511			keys_add(request_keys, "DataDigest", "None");
512
513		keys_add(request_keys, "ImmediateData", "Yes");
514		keys_add_int(request_keys, "MaxBurstLength",
515		    isl->isl_max_burst_length);
516		keys_add_int(request_keys, "FirstBurstLength",
517		    isl->isl_first_burst_length);
518		keys_add(request_keys, "InitialR2T", "Yes");
519		keys_add(request_keys, "MaxOutstandingR2T", "1");
520		if (conn->conn_conf.isc_iser == 1) {
521			keys_add_int(request_keys, "InitiatorRecvDataSegmentLength",
522			    isl->isl_max_recv_data_segment_length);
523			keys_add_int(request_keys, "TargetRecvDataSegmentLength",
524			    isl->isl_max_send_data_segment_length);
525			keys_add(request_keys, "RDMAExtensions", "Yes");
526		} else {
527			keys_add_int(request_keys, "MaxRecvDataSegmentLength",
528			    isl->isl_max_recv_data_segment_length);
529		}
530	} else {
531		keys_add(request_keys, "HeaderDigest", "None");
532		keys_add(request_keys, "DataDigest", "None");
533		keys_add_int(request_keys, "MaxRecvDataSegmentLength",
534		    isl->isl_max_recv_data_segment_length);
535	}
536
537	conn->conn.conn_max_recv_data_segment_length =
538	    isl->isl_max_recv_data_segment_length;
539
540	keys_add(request_keys, "DefaultTime2Wait", "0");
541	keys_add(request_keys, "DefaultTime2Retain", "0");
542	keys_add(request_keys, "ErrorRecoveryLevel", "0");
543	keys_save_pdu(request_keys, request);
544	keys_delete(request_keys);
545	request_keys = NULL;
546	pdu_send(request);
547	pdu_delete(request);
548	request = NULL;
549
550	response = login_receive(&conn->conn);
551	response_keys = keys_new();
552	keys_load_pdu(response_keys, response);
553	for (i = 0; i < KEYS_MAX; i++) {
554		if (response_keys->keys_names[i] == NULL)
555			break;
556
557		login_negotiate_key(conn,
558		    response_keys->keys_names[i], response_keys->keys_values[i]);
559	}
560
561	keys_delete(response_keys);
562	response_keys = NULL;
563
564	for (;;) {
565		bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
566		if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0)
567			break;
568
569		nrequests++;
570		if (nrequests > 5) {
571			log_warnx("received login response "
572			    "without the \"T\" flag too many times; giving up");
573			break;
574		}
575
576		log_debugx("received login response "
577		    "without the \"T\" flag; sending another request");
578
579		pdu_delete(response);
580
581		request = login_new_request(&conn->conn,
582		    BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
583		pdu_send(request);
584		pdu_delete(request);
585
586		response = login_receive(&conn->conn);
587	}
588
589	if (login_nsg(response) != BHSLR_STAGE_FULL_FEATURE_PHASE)
590		log_warnx("received final login response with wrong NSG 0x%x",
591		    login_nsg(response));
592	pdu_delete(response);
593
594	log_debugx("operational parameter negotiation done; "
595	    "transitioning to Full Feature phase");
596}
597
598static void
599login_send_chap_a(struct connection *conn)
600{
601	struct pdu *request;
602	struct keys *request_keys;
603
604	request = login_new_request(conn, BHSLR_STAGE_SECURITY_NEGOTIATION);
605	request_keys = keys_new();
606	keys_add(request_keys, "CHAP_A", "5");
607	keys_save_pdu(request_keys, request);
608	keys_delete(request_keys);
609	pdu_send(request);
610	pdu_delete(request);
611}
612
613static void
614login_send_chap_r(struct pdu *response)
615{
616	struct iscsid_connection *conn;
617	struct pdu *request;
618	struct keys *request_keys, *response_keys;
619	struct rchap *rchap;
620	const char *chap_a, *chap_c, *chap_i;
621	char *chap_r;
622	int error;
623        char *mutual_chap_c, *mutual_chap_i;
624
625	/*
626	 * As in the rest of the initiator, 'request' means
627	 * 'initiator -> target', and 'response' means 'target -> initiator',
628	 *
629	 * So, here the 'response' from the target is the packet that contains
630	 * CHAP challenge; our CHAP response goes into 'request'.
631	 */
632
633	conn = (struct iscsid_connection *)response->pdu_connection;
634
635	response_keys = keys_new();
636	keys_load_pdu(response_keys, response);
637
638	/*
639	 * First, compute the response.
640	 */
641	chap_a = keys_find(response_keys, "CHAP_A");
642	if (chap_a == NULL)
643		log_errx(1, "received CHAP packet without CHAP_A");
644	chap_c = keys_find(response_keys, "CHAP_C");
645	if (chap_c == NULL)
646		log_errx(1, "received CHAP packet without CHAP_C");
647	chap_i = keys_find(response_keys, "CHAP_I");
648	if (chap_i == NULL)
649		log_errx(1, "received CHAP packet without CHAP_I");
650
651	if (strcmp(chap_a, "5") != 0) {
652		log_errx(1, "received CHAP packet "
653		    "with unsupported CHAP_A \"%s\"", chap_a);
654	}
655
656	rchap = rchap_new(conn->conn_conf.isc_secret);
657	error = rchap_receive(rchap, chap_i, chap_c);
658	if (error != 0) {
659		log_errx(1, "received CHAP packet "
660		    "with malformed CHAP_I or CHAP_C");
661	}
662	chap_r = rchap_get_response(rchap);
663	rchap_delete(rchap);
664
665	keys_delete(response_keys);
666
667	request = login_new_request(&conn->conn,
668	    BHSLR_STAGE_SECURITY_NEGOTIATION);
669	request_keys = keys_new();
670	keys_add(request_keys, "CHAP_N", conn->conn_conf.isc_user);
671	keys_add(request_keys, "CHAP_R", chap_r);
672	free(chap_r);
673
674	/*
675	 * If we want mutual authentication, we're expected to send
676	 * our CHAP_I/CHAP_C now.
677	 */
678	if (conn->conn_conf.isc_mutual_user[0] != '\0') {
679		log_debugx("requesting mutual authentication; "
680		    "binary challenge size is %zd bytes",
681		    sizeof(conn->conn_mutual_chap->chap_challenge));
682
683		assert(conn->conn_mutual_chap == NULL);
684		conn->conn_mutual_chap = chap_new();
685		mutual_chap_i = chap_get_id(conn->conn_mutual_chap);
686		mutual_chap_c = chap_get_challenge(conn->conn_mutual_chap);
687		keys_add(request_keys, "CHAP_I", mutual_chap_i);
688		keys_add(request_keys, "CHAP_C", mutual_chap_c);
689		free(mutual_chap_i);
690		free(mutual_chap_c);
691	}
692
693	keys_save_pdu(request_keys, request);
694	keys_delete(request_keys);
695	pdu_send(request);
696	pdu_delete(request);
697}
698
699static void
700login_verify_mutual(const struct pdu *response)
701{
702	struct iscsid_connection *conn;
703	struct keys *response_keys;
704	const char *chap_n, *chap_r;
705	int error;
706
707	conn = (struct iscsid_connection *)response->pdu_connection;
708
709	response_keys = keys_new();
710	keys_load_pdu(response_keys, response);
711
712        chap_n = keys_find(response_keys, "CHAP_N");
713        if (chap_n == NULL)
714                log_errx(1, "received CHAP Response PDU without CHAP_N");
715        chap_r = keys_find(response_keys, "CHAP_R");
716        if (chap_r == NULL)
717                log_errx(1, "received CHAP Response PDU without CHAP_R");
718
719	error = chap_receive(conn->conn_mutual_chap, chap_r);
720	if (error != 0)
721                log_errx(1, "received CHAP Response PDU with invalid CHAP_R");
722
723	if (strcmp(chap_n, conn->conn_conf.isc_mutual_user) != 0) {
724		fail(&conn->conn, "Mutual CHAP failed");
725		log_errx(1, "mutual CHAP authentication failed: wrong user");
726	}
727
728	error = chap_authenticate(conn->conn_mutual_chap,
729	    conn->conn_conf.isc_mutual_secret);
730	if (error != 0) {
731		fail(&conn->conn, "Mutual CHAP failed");
732                log_errx(1, "mutual CHAP authentication failed: wrong secret");
733	}
734
735	keys_delete(response_keys);
736	chap_delete(conn->conn_mutual_chap);
737	conn->conn_mutual_chap = NULL;
738
739	log_debugx("mutual CHAP authentication succeeded");
740}
741
742static void
743login_chap(struct iscsid_connection *conn)
744{
745	struct pdu *response;
746
747	log_debugx("beginning CHAP authentication; sending CHAP_A");
748	login_send_chap_a(&conn->conn);
749
750	log_debugx("waiting for CHAP_A/CHAP_C/CHAP_I");
751	response = login_receive(&conn->conn);
752
753	log_debugx("sending CHAP_N/CHAP_R");
754	login_send_chap_r(response);
755	pdu_delete(response);
756
757	/*
758	 * XXX: Make sure this is not susceptible to MITM.
759	 */
760
761	log_debugx("waiting for CHAP result");
762	response = login_receive(&conn->conn);
763	if (conn->conn_conf.isc_mutual_user[0] != '\0')
764		login_verify_mutual(response);
765	pdu_delete(response);
766
767	log_debugx("CHAP authentication done");
768}
769
770void
771login(struct iscsid_connection *conn)
772{
773	struct pdu *request, *response;
774	struct keys *request_keys, *response_keys;
775	struct iscsi_bhs_login_response *bhslr2;
776	const char *auth_method;
777	int i;
778
779	log_debugx("beginning Login phase; sending Login PDU");
780	request = login_new_request(&conn->conn,
781	    BHSLR_STAGE_SECURITY_NEGOTIATION);
782	request_keys = keys_new();
783	if (conn->conn_conf.isc_mutual_user[0] != '\0') {
784		keys_add(request_keys, "AuthMethod", "CHAP");
785	} else if (conn->conn_conf.isc_user[0] != '\0') {
786		/*
787		 * Give target a chance to skip authentication if it
788		 * doesn't feel like it.
789		 *
790		 * None is first, CHAP second; this is to work around
791		 * what seems to be LIO (Linux target) bug: otherwise,
792		 * if target is configured with no authentication,
793		 * and we are configured to authenticate, the target
794		 * will erroneously respond with AuthMethod=CHAP
795		 * instead of AuthMethod=None, and will subsequently
796		 * fail the connection.  This usually happens with
797		 * Discovery sessions, which default to no authentication.
798		 */
799		keys_add(request_keys, "AuthMethod", "None,CHAP");
800	} else {
801		keys_add(request_keys, "AuthMethod", "None");
802	}
803	keys_add(request_keys, "InitiatorName",
804	    conn->conn_conf.isc_initiator);
805	if (conn->conn_conf.isc_initiator_alias[0] != '\0') {
806		keys_add(request_keys, "InitiatorAlias",
807		    conn->conn_conf.isc_initiator_alias);
808	}
809	if (conn->conn_conf.isc_discovery == 0) {
810		keys_add(request_keys, "SessionType", "Normal");
811		keys_add(request_keys,
812		    "TargetName", conn->conn_conf.isc_target);
813	} else {
814		keys_add(request_keys, "SessionType", "Discovery");
815	}
816	keys_save_pdu(request_keys, request);
817	keys_delete(request_keys);
818	pdu_send(request);
819	pdu_delete(request);
820
821	response = login_receive(&conn->conn);
822
823	response_keys = keys_new();
824	keys_load_pdu(response_keys, response);
825
826	for (i = 0; i < KEYS_MAX; i++) {
827		if (response_keys->keys_names[i] == NULL)
828			break;
829
830		/*
831		 * Not interested in AuthMethod at this point; we only need
832		 * to parse things such as TargetAlias.
833		 *
834		 * XXX: This is somewhat ugly.  We should have a way to apply
835		 *      all the keys to the session and use that by default
836		 *      instead of discarding them.
837		 */
838		if (strcmp(response_keys->keys_names[i], "AuthMethod") == 0)
839			continue;
840
841		login_negotiate_key(conn,
842		    response_keys->keys_names[i], response_keys->keys_values[i]);
843	}
844
845	bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
846	if ((bhslr2->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0 &&
847	    login_nsg(response) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) {
848		if (conn->conn_conf.isc_mutual_user[0] != '\0') {
849			log_errx(1, "target requested transition "
850			    "to operational parameter negotiation, "
851			    "but we require mutual CHAP");
852		}
853
854		log_debugx("target requested transition "
855		    "to operational parameter negotiation");
856		keys_delete(response_keys);
857		pdu_delete(response);
858		login_negotiate(conn);
859		return;
860	}
861
862	auth_method = keys_find(response_keys, "AuthMethod");
863	if (auth_method == NULL)
864		log_errx(1, "received response without AuthMethod");
865	if (strcmp(auth_method, "None") == 0) {
866		if (conn->conn_conf.isc_mutual_user[0] != '\0') {
867			log_errx(1, "target does not require authantication, "
868			    "but we require mutual CHAP");
869		}
870
871		log_debugx("target does not require authentication");
872		keys_delete(response_keys);
873		pdu_delete(response);
874		login_negotiate(conn);
875		return;
876	}
877
878	if (strcmp(auth_method, "CHAP") != 0) {
879		fail(&conn->conn, "Unsupported AuthMethod");
880		log_errx(1, "received response "
881		    "with unsupported AuthMethod \"%s\"", auth_method);
882	}
883
884	if (conn->conn_conf.isc_user[0] == '\0' ||
885	    conn->conn_conf.isc_secret[0] == '\0') {
886		fail(&conn->conn, "Authentication required");
887		log_errx(1, "target requests CHAP authentication, but we don't "
888		    "have user and secret");
889	}
890
891	keys_delete(response_keys);
892	response_keys = NULL;
893	pdu_delete(response);
894	response = NULL;
895
896	login_chap(conn);
897	login_negotiate(conn);
898}
899