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