1255570Strasz/*-
2255570Strasz * Copyright (c) 2012 The FreeBSD Foundation
3255570Strasz * All rights reserved.
4255570Strasz *
5255570Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
6255570Strasz * from the FreeBSD Foundation.
7255570Strasz *
8255570Strasz * Redistribution and use in source and binary forms, with or without
9255570Strasz * modification, are permitted provided that the following conditions
10255570Strasz * are met:
11255570Strasz * 1. Redistributions of source code must retain the above copyright
12255570Strasz *    notice, this list of conditions and the following disclaimer.
13255570Strasz * 2. Redistributions in binary form must reproduce the above copyright
14255570Strasz *    notice, this list of conditions and the following disclaimer in the
15255570Strasz *    documentation and/or other materials provided with the distribution.
16255570Strasz *
17255570Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20255570Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27255570Strasz * SUCH DAMAGE.
28255570Strasz *
29255570Strasz */
30255570Strasz
31270888Strasz#include <sys/cdefs.h>
32270888Strasz__FBSDID("$FreeBSD$");
33270888Strasz
34255570Strasz#include <assert.h>
35255570Strasz#include <stdbool.h>
36255570Strasz#include <stdint.h>
37255570Strasz#include <stdio.h>
38255570Strasz#include <stdlib.h>
39255570Strasz#include <string.h>
40255570Strasz#include <unistd.h>
41255570Strasz#include <netinet/in.h>
42255570Strasz#include <openssl/err.h>
43255570Strasz#include <openssl/md5.h>
44255570Strasz#include <openssl/rand.h>
45255570Strasz
46255570Strasz#include "ctld.h"
47255570Strasz#include "iscsi_proto.h"
48255570Strasz
49255570Straszstatic void login_send_error(struct pdu *request,
50255570Strasz    char class, char detail);
51255570Strasz
52255570Straszstatic void
53255570Straszlogin_set_nsg(struct pdu *response, int nsg)
54255570Strasz{
55255570Strasz	struct iscsi_bhs_login_response *bhslr;
56255570Strasz
57255570Strasz	assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
58255570Strasz	    nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
59255570Strasz	    nsg == BHSLR_STAGE_FULL_FEATURE_PHASE);
60255570Strasz
61255570Strasz	bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
62255570Strasz
63255570Strasz	bhslr->bhslr_flags &= 0xFC;
64255570Strasz	bhslr->bhslr_flags |= nsg;
65255570Strasz}
66255570Strasz
67255570Straszstatic int
68255570Straszlogin_csg(const struct pdu *request)
69255570Strasz{
70255570Strasz	struct iscsi_bhs_login_request *bhslr;
71255570Strasz
72255570Strasz	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
73255570Strasz
74255570Strasz	return ((bhslr->bhslr_flags & 0x0C) >> 2);
75255570Strasz}
76255570Strasz
77255570Straszstatic void
78255570Straszlogin_set_csg(struct pdu *response, int csg)
79255570Strasz{
80255570Strasz	struct iscsi_bhs_login_response *bhslr;
81255570Strasz
82255570Strasz	assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
83255570Strasz	    csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
84255570Strasz	    csg == BHSLR_STAGE_FULL_FEATURE_PHASE);
85255570Strasz
86255570Strasz	bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs;
87255570Strasz
88255570Strasz	bhslr->bhslr_flags &= 0xF3;
89255570Strasz	bhslr->bhslr_flags |= csg << 2;
90255570Strasz}
91255570Strasz
92255570Straszstatic struct pdu *
93255570Straszlogin_receive(struct connection *conn, bool initial)
94255570Strasz{
95255570Strasz	struct pdu *request;
96255570Strasz	struct iscsi_bhs_login_request *bhslr;
97255570Strasz
98255570Strasz	request = pdu_new(conn);
99255570Strasz	pdu_receive(request);
100255570Strasz	if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
101255570Strasz	    ISCSI_BHS_OPCODE_LOGIN_REQUEST) {
102255570Strasz		/*
103255570Strasz		 * The first PDU in session is special - if we receive any PDU
104255570Strasz		 * different than login request, we have to drop the connection
105255570Strasz		 * without sending response ("A target receiving any PDU
106255570Strasz		 * except a Login request before the Login Phase is started MUST
107255570Strasz		 * immediately terminate the connection on which the PDU
108255570Strasz		 * was received.")
109255570Strasz		 */
110255570Strasz		if (initial == false)
111255570Strasz			login_send_error(request, 0x02, 0x0b);
112255570Strasz		log_errx(1, "protocol error: received invalid opcode 0x%x",
113255570Strasz		    request->pdu_bhs->bhs_opcode);
114255570Strasz	}
115255570Strasz	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
116255570Strasz	/*
117255570Strasz	 * XXX: Implement the C flag some day.
118255570Strasz	 */
119255570Strasz	if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) {
120255570Strasz		login_send_error(request, 0x03, 0x00);
121255570Strasz		log_errx(1, "received Login PDU with unsupported \"C\" flag");
122255570Strasz	}
123255570Strasz	if (bhslr->bhslr_version_max != 0x00) {
124255570Strasz		login_send_error(request, 0x02, 0x05);
125255570Strasz		log_errx(1, "received Login PDU with unsupported "
126255570Strasz		    "Version-max 0x%x", bhslr->bhslr_version_max);
127255570Strasz	}
128255570Strasz	if (bhslr->bhslr_version_min != 0x00) {
129255570Strasz		login_send_error(request, 0x02, 0x05);
130255570Strasz		log_errx(1, "received Login PDU with unsupported "
131255570Strasz		    "Version-min 0x%x", bhslr->bhslr_version_min);
132255570Strasz	}
133255570Strasz	if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) {
134255570Strasz		login_send_error(request, 0x02, 0x05);
135255570Strasz		log_errx(1, "received Login PDU with decreasing CmdSN: "
136255570Strasz		    "was %d, is %d", conn->conn_cmdsn,
137255570Strasz		    ntohl(bhslr->bhslr_cmdsn));
138255570Strasz	}
139255570Strasz	if (initial == false &&
140255570Strasz	    ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) {
141255570Strasz		login_send_error(request, 0x02, 0x05);
142255570Strasz		log_errx(1, "received Login PDU with wrong ExpStatSN: "
143255570Strasz		    "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn),
144255570Strasz		    conn->conn_statsn);
145255570Strasz	}
146255570Strasz	conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn);
147255570Strasz
148255570Strasz	return (request);
149255570Strasz}
150255570Strasz
151255570Straszstatic struct pdu *
152255570Straszlogin_new_response(struct pdu *request)
153255570Strasz{
154255570Strasz	struct pdu *response;
155255570Strasz	struct connection *conn;
156255570Strasz	struct iscsi_bhs_login_request *bhslr;
157255570Strasz	struct iscsi_bhs_login_response *bhslr2;
158255570Strasz
159255570Strasz	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
160255570Strasz	conn = request->pdu_connection;
161255570Strasz
162255570Strasz	response = pdu_new_response(request);
163255570Strasz	bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
164255570Strasz	bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_RESPONSE;
165255570Strasz	login_set_csg(response, BHSLR_STAGE_SECURITY_NEGOTIATION);
166255570Strasz	memcpy(bhslr2->bhslr_isid,
167255570Strasz	    bhslr->bhslr_isid, sizeof(bhslr2->bhslr_isid));
168255570Strasz	bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag;
169255570Strasz	bhslr2->bhslr_statsn = htonl(conn->conn_statsn++);
170255570Strasz	bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn);
171255570Strasz	bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn);
172255570Strasz
173255570Strasz	return (response);
174255570Strasz}
175255570Strasz
176255570Straszstatic void
177255570Straszlogin_send_error(struct pdu *request, char class, char detail)
178255570Strasz{
179255570Strasz	struct pdu *response;
180255570Strasz	struct iscsi_bhs_login_response *bhslr2;
181255570Strasz
182255570Strasz	log_debugx("sending Login Response PDU with failure class 0x%x/0x%x; "
183255570Strasz	    "see next line for reason", class, detail);
184255570Strasz	response = login_new_response(request);
185255570Strasz	bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
186255570Strasz	bhslr2->bhslr_status_class = class;
187255570Strasz	bhslr2->bhslr_status_detail = detail;
188255570Strasz
189255570Strasz	pdu_send(response);
190255570Strasz	pdu_delete(response);
191255570Strasz}
192255570Strasz
193255570Straszstatic int
194255570Straszlogin_list_contains(const char *list, const char *what)
195255570Strasz{
196255570Strasz	char *tofree, *str, *token;
197255570Strasz
198255570Strasz	tofree = str = checked_strdup(list);
199255570Strasz
200255570Strasz	while ((token = strsep(&str, ",")) != NULL) {
201255570Strasz		if (strcmp(token, what) == 0) {
202255570Strasz			free(tofree);
203255570Strasz			return (1);
204255570Strasz		}
205255570Strasz	}
206255570Strasz	free(tofree);
207255570Strasz	return (0);
208255570Strasz}
209255570Strasz
210255570Straszstatic int
211255570Straszlogin_list_prefers(const char *list,
212255570Strasz    const char *choice1, const char *choice2)
213255570Strasz{
214255570Strasz	char *tofree, *str, *token;
215255570Strasz
216255570Strasz	tofree = str = checked_strdup(list);
217255570Strasz
218255570Strasz	while ((token = strsep(&str, ",")) != NULL) {
219255570Strasz		if (strcmp(token, choice1) == 0) {
220255570Strasz			free(tofree);
221255570Strasz			return (1);
222255570Strasz		}
223255570Strasz		if (strcmp(token, choice2) == 0) {
224255570Strasz			free(tofree);
225255570Strasz			return (2);
226255570Strasz		}
227255570Strasz	}
228255570Strasz	free(tofree);
229255570Strasz	return (-1);
230255570Strasz}
231255570Strasz
232255570Straszstatic int
233255570Straszlogin_hex2int(const char hex)
234255570Strasz{
235255570Strasz	switch (hex) {
236255570Strasz	case '0':
237255570Strasz		return (0x00);
238255570Strasz	case '1':
239255570Strasz		return (0x01);
240255570Strasz	case '2':
241255570Strasz		return (0x02);
242255570Strasz	case '3':
243255570Strasz		return (0x03);
244255570Strasz	case '4':
245255570Strasz		return (0x04);
246255570Strasz	case '5':
247255570Strasz		return (0x05);
248255570Strasz	case '6':
249255570Strasz		return (0x06);
250255570Strasz	case '7':
251255570Strasz		return (0x07);
252255570Strasz	case '8':
253255570Strasz		return (0x08);
254255570Strasz	case '9':
255255570Strasz		return (0x09);
256255570Strasz	case 'a':
257255570Strasz	case 'A':
258255570Strasz		return (0x0a);
259255570Strasz	case 'b':
260255570Strasz	case 'B':
261255570Strasz		return (0x0b);
262255570Strasz	case 'c':
263255570Strasz	case 'C':
264255570Strasz		return (0x0c);
265255570Strasz	case 'd':
266255570Strasz	case 'D':
267255570Strasz		return (0x0d);
268255570Strasz	case 'e':
269255570Strasz	case 'E':
270255570Strasz		return (0x0e);
271255570Strasz	case 'f':
272255570Strasz	case 'F':
273255570Strasz		return (0x0f);
274255570Strasz	default:
275255570Strasz		return (-1);
276255570Strasz	}
277255570Strasz}
278255570Strasz
279255570Strasz/*
280255570Strasz * XXX: Review this _carefully_.
281255570Strasz */
282255570Straszstatic int
283255570Straszlogin_hex2bin(const char *hex, char **binp, size_t *bin_lenp)
284255570Strasz{
285255570Strasz	int i, hex_len, nibble;
286255570Strasz	bool lo = true; /* As opposed to 'hi'. */
287255570Strasz	char *bin;
288255570Strasz	size_t bin_off, bin_len;
289255570Strasz
290255570Strasz	if (strncasecmp(hex, "0x", strlen("0x")) != 0) {
291255570Strasz		log_warnx("malformed variable, should start with \"0x\"");
292255570Strasz		return (-1);
293255570Strasz	}
294255570Strasz
295255570Strasz	hex += strlen("0x");
296255570Strasz	hex_len = strlen(hex);
297255570Strasz	if (hex_len < 1) {
298255570Strasz		log_warnx("malformed variable; doesn't contain anything "
299255570Strasz		    "but \"0x\"");
300255570Strasz		return (-1);
301255570Strasz	}
302255570Strasz
303255570Strasz	bin_len = hex_len / 2 + hex_len % 2;
304255570Strasz	bin = calloc(bin_len, 1);
305255570Strasz	if (bin == NULL)
306255570Strasz		log_err(1, "calloc");
307255570Strasz
308255570Strasz	bin_off = bin_len - 1;
309255570Strasz	for (i = hex_len - 1; i >= 0; i--) {
310255570Strasz		nibble = login_hex2int(hex[i]);
311255570Strasz		if (nibble < 0) {
312255570Strasz			log_warnx("malformed variable, invalid char \"%c\"",
313255570Strasz			    hex[i]);
314255570Strasz			return (-1);
315255570Strasz		}
316255570Strasz
317255570Strasz		assert(bin_off < bin_len);
318255570Strasz		if (lo) {
319255570Strasz			bin[bin_off] = nibble;
320255570Strasz			lo = false;
321255570Strasz		} else {
322255570Strasz			bin[bin_off] |= nibble << 4;
323255570Strasz			bin_off--;
324255570Strasz			lo = true;
325255570Strasz		}
326255570Strasz	}
327255570Strasz
328255570Strasz	*binp = bin;
329255570Strasz	*bin_lenp = bin_len;
330255570Strasz	return (0);
331255570Strasz}
332255570Strasz
333255570Straszstatic char *
334255570Straszlogin_bin2hex(const char *bin, size_t bin_len)
335255570Strasz{
336255570Strasz	unsigned char *hex, *tmp, ch;
337255570Strasz	size_t hex_len;
338255570Strasz	size_t i;
339255570Strasz
340255570Strasz	hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */
341255570Strasz	hex = malloc(hex_len);
342255570Strasz	if (hex == NULL)
343255570Strasz		log_err(1, "malloc");
344255570Strasz
345255570Strasz	tmp = hex;
346255570Strasz	tmp += sprintf(tmp, "0x");
347255570Strasz	for (i = 0; i < bin_len; i++) {
348255570Strasz		ch = bin[i];
349255570Strasz		tmp += sprintf(tmp, "%02x", ch);
350255570Strasz	}
351255570Strasz
352255570Strasz	return (hex);
353255570Strasz}
354255570Strasz
355255570Straszstatic void
356255570Straszlogin_compute_md5(const char id, const char *secret,
357255570Strasz    const void *challenge, size_t challenge_len, void *response,
358255570Strasz    size_t response_len)
359255570Strasz{
360255570Strasz	MD5_CTX ctx;
361255570Strasz	int rv;
362255570Strasz
363255570Strasz	assert(response_len == MD5_DIGEST_LENGTH);
364255570Strasz
365255570Strasz	MD5_Init(&ctx);
366255570Strasz	MD5_Update(&ctx, &id, sizeof(id));
367255570Strasz	MD5_Update(&ctx, secret, strlen(secret));
368255570Strasz	MD5_Update(&ctx, challenge, challenge_len);
369255570Strasz	rv = MD5_Final(response, &ctx);
370255570Strasz	if (rv != 1)
371255570Strasz		log_errx(1, "MD5_Final");
372255570Strasz}
373255570Strasz
374255570Strasz#define	LOGIN_CHALLENGE_LEN	1024
375255570Strasz
376255570Straszstatic struct pdu *
377255570Straszlogin_receive_chap_a(struct connection *conn)
378255570Strasz{
379255570Strasz	struct pdu *request;
380255570Strasz	struct keys *request_keys;
381255570Strasz	const char *chap_a;
382255570Strasz
383255570Strasz	request = login_receive(conn, false);
384255570Strasz	request_keys = keys_new();
385255570Strasz	keys_load(request_keys, request);
386255570Strasz
387255570Strasz	chap_a = keys_find(request_keys, "CHAP_A");
388255570Strasz	if (chap_a == NULL) {
389255570Strasz		login_send_error(request, 0x02, 0x07);
390255570Strasz		log_errx(1, "received CHAP Login PDU without CHAP_A");
391255570Strasz	}
392255570Strasz	if (login_list_contains(chap_a, "5") == 0) {
393255570Strasz		login_send_error(request, 0x02, 0x01);
394255570Strasz		log_errx(1, "received CHAP Login PDU with unsupported CHAP_A "
395255570Strasz		    "\"%s\"", chap_a);
396255570Strasz	}
397255570Strasz	keys_delete(request_keys);
398255570Strasz
399255570Strasz	return (request);
400255570Strasz}
401255570Strasz
402255570Straszstatic void
403255570Straszlogin_send_chap_c(struct pdu *request, const unsigned char id,
404255570Strasz    const void *challenge, const size_t challenge_len)
405255570Strasz{
406255570Strasz	struct pdu *response;
407255570Strasz	struct keys *response_keys;
408255570Strasz	char *chap_c, chap_i[4];
409255570Strasz
410255570Strasz	chap_c = login_bin2hex(challenge, challenge_len);
411255570Strasz	snprintf(chap_i, sizeof(chap_i), "%d", id);
412255570Strasz
413255570Strasz	response = login_new_response(request);
414255570Strasz	response_keys = keys_new();
415255570Strasz	keys_add(response_keys, "CHAP_A", "5");
416255570Strasz	keys_add(response_keys, "CHAP_I", chap_i);
417255570Strasz	keys_add(response_keys, "CHAP_C", chap_c);
418255570Strasz	free(chap_c);
419255570Strasz	keys_save(response_keys, response);
420256192Strasz	pdu_send(response);
421256192Strasz	pdu_delete(response);
422255570Strasz	keys_delete(response_keys);
423255570Strasz}
424255570Strasz
425255570Straszstatic struct pdu *
426255570Straszlogin_receive_chap_r(struct connection *conn,
427255570Strasz    struct auth_group *ag, const unsigned char id, const void *challenge,
428255570Strasz    const size_t challenge_len, const struct auth **cap)
429255570Strasz{
430255570Strasz	struct pdu *request;
431255570Strasz	struct keys *request_keys;
432255570Strasz	const char *chap_n, *chap_r;
433255570Strasz	char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH];
434255570Strasz	size_t response_bin_len;
435255570Strasz	const struct auth *auth;
436255570Strasz	int error;
437255570Strasz
438255570Strasz	request = login_receive(conn, false);
439255570Strasz	request_keys = keys_new();
440255570Strasz	keys_load(request_keys, request);
441255570Strasz
442255570Strasz	chap_n = keys_find(request_keys, "CHAP_N");
443255570Strasz	if (chap_n == NULL) {
444255570Strasz		login_send_error(request, 0x02, 0x07);
445255570Strasz		log_errx(1, "received CHAP Login PDU without CHAP_N");
446255570Strasz	}
447255570Strasz	chap_r = keys_find(request_keys, "CHAP_R");
448255570Strasz	if (chap_r == NULL) {
449255570Strasz		login_send_error(request, 0x02, 0x07);
450255570Strasz		log_errx(1, "received CHAP Login PDU without CHAP_R");
451255570Strasz	}
452255570Strasz	error = login_hex2bin(chap_r, &response_bin, &response_bin_len);
453255570Strasz	if (error != 0) {
454255570Strasz		login_send_error(request, 0x02, 0x07);
455255570Strasz		log_errx(1, "received CHAP Login PDU with malformed CHAP_R");
456255570Strasz	}
457255570Strasz
458255570Strasz	/*
459255570Strasz	 * Verify the response.
460255570Strasz	 */
461255570Strasz	assert(ag->ag_type == AG_TYPE_CHAP ||
462255570Strasz	    ag->ag_type == AG_TYPE_CHAP_MUTUAL);
463255570Strasz	auth = auth_find(ag, chap_n);
464255570Strasz	if (auth == NULL) {
465255570Strasz		login_send_error(request, 0x02, 0x01);
466255570Strasz		log_errx(1, "received CHAP Login with invalid user \"%s\"",
467255570Strasz		    chap_n);
468255570Strasz	}
469255570Strasz
470255570Strasz	assert(auth->a_secret != NULL);
471255570Strasz	assert(strlen(auth->a_secret) > 0);
472255570Strasz	login_compute_md5(id, auth->a_secret, challenge,
473255570Strasz	    challenge_len, expected_response_bin,
474255570Strasz	    sizeof(expected_response_bin));
475255570Strasz
476255570Strasz	if (memcmp(response_bin, expected_response_bin,
477255570Strasz	    sizeof(expected_response_bin)) != 0) {
478255570Strasz		login_send_error(request, 0x02, 0x01);
479255570Strasz		log_errx(1, "CHAP authentication failed for user \"%s\"",
480255570Strasz		    auth->a_user);
481255570Strasz	}
482255570Strasz
483255570Strasz	keys_delete(request_keys);
484255570Strasz	free(response_bin);
485255570Strasz
486255570Strasz	*cap = auth;
487255570Strasz	return (request);
488255570Strasz}
489255570Strasz
490255570Straszstatic void
491255570Straszlogin_send_chap_success(struct pdu *request,
492255570Strasz    const struct auth *auth)
493255570Strasz{
494255570Strasz	struct pdu *response;
495255570Strasz	struct keys *request_keys, *response_keys;
496255570Strasz	struct iscsi_bhs_login_response *bhslr2;
497255570Strasz	const char *chap_i, *chap_c;
498255570Strasz	char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH];
499255570Strasz	size_t challenge_len;
500255570Strasz	unsigned char id;
501255570Strasz	int error;
502255570Strasz
503255570Strasz	response = login_new_response(request);
504255570Strasz	bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
505255570Strasz	bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT;
506255570Strasz	login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
507255570Strasz
508255570Strasz	/*
509255570Strasz	 * Actually, one more thing: mutual authentication.
510255570Strasz	 */
511255570Strasz	request_keys = keys_new();
512255570Strasz	keys_load(request_keys, request);
513255570Strasz	chap_i = keys_find(request_keys, "CHAP_I");
514255570Strasz	chap_c = keys_find(request_keys, "CHAP_C");
515255570Strasz	if (chap_i != NULL || chap_c != NULL) {
516255570Strasz		if (chap_i == NULL) {
517255570Strasz			login_send_error(request, 0x02, 0x07);
518255570Strasz			log_errx(1, "initiator requested target "
519255570Strasz			    "authentication, but didn't send CHAP_I");
520255570Strasz		}
521255570Strasz		if (chap_c == NULL) {
522255570Strasz			login_send_error(request, 0x02, 0x07);
523255570Strasz			log_errx(1, "initiator requested target "
524255570Strasz			    "authentication, but didn't send CHAP_C");
525255570Strasz		}
526255570Strasz		if (auth->a_auth_group->ag_type != AG_TYPE_CHAP_MUTUAL) {
527255570Strasz			login_send_error(request, 0x02, 0x01);
528255570Strasz			log_errx(1, "initiator requests target authentication "
529255570Strasz			    "for user \"%s\", but mutual user/secret "
530255570Strasz			    "is not set", auth->a_user);
531255570Strasz		}
532255570Strasz
533255570Strasz		id = strtoul(chap_i, NULL, 10);
534255570Strasz		error = login_hex2bin(chap_c, &challenge, &challenge_len);
535255570Strasz		if (error != 0) {
536255570Strasz			login_send_error(request, 0x02, 0x07);
537255570Strasz			log_errx(1, "received CHAP Login PDU with malformed "
538255570Strasz			    "CHAP_C");
539255570Strasz		}
540255570Strasz
541255570Strasz		log_debugx("performing mutual authentication as user \"%s\"",
542255570Strasz		    auth->a_mutual_user);
543255570Strasz		login_compute_md5(id, auth->a_mutual_secret, challenge,
544255570Strasz		    challenge_len, response_bin, sizeof(response_bin));
545255570Strasz
546255570Strasz		chap_r = login_bin2hex(response_bin,
547255570Strasz		    sizeof(response_bin));
548255570Strasz		response_keys = keys_new();
549255570Strasz		keys_add(response_keys, "CHAP_N", auth->a_mutual_user);
550255570Strasz		keys_add(response_keys, "CHAP_R", chap_r);
551255570Strasz		free(chap_r);
552255570Strasz		keys_save(response_keys, response);
553255570Strasz		keys_delete(response_keys);
554255570Strasz	} else {
555255570Strasz		log_debugx("initiator did not request target authentication");
556255570Strasz	}
557255570Strasz
558255570Strasz	keys_delete(request_keys);
559255570Strasz	pdu_send(response);
560256192Strasz	pdu_delete(response);
561255570Strasz}
562255570Strasz
563255570Straszstatic void
564255570Straszlogin_chap(struct connection *conn, struct auth_group *ag)
565255570Strasz{
566255570Strasz	const struct auth *auth;
567255570Strasz	struct pdu *request;
568255570Strasz	char challenge_bin[LOGIN_CHALLENGE_LEN];
569255570Strasz	unsigned char id;
570255570Strasz	int rv;
571255570Strasz
572255570Strasz	/*
573255570Strasz	 * Receive CHAP_A PDU.
574255570Strasz	 */
575255570Strasz	log_debugx("beginning CHAP authentication; waiting for CHAP_A");
576255570Strasz	request = login_receive_chap_a(conn);
577255570Strasz
578255570Strasz	/*
579255570Strasz	 * Generate the challenge.
580255570Strasz	 */
581255570Strasz	rv = RAND_bytes(challenge_bin, sizeof(challenge_bin));
582255570Strasz	if (rv != 1) {
583255570Strasz		login_send_error(request, 0x03, 0x02);
584255570Strasz		log_errx(1, "RAND_bytes failed: %s",
585255570Strasz		    ERR_error_string(ERR_get_error(), NULL));
586255570Strasz	}
587255570Strasz	rv = RAND_bytes(&id, sizeof(id));
588255570Strasz	if (rv != 1) {
589255570Strasz		login_send_error(request, 0x03, 0x02);
590255570Strasz		log_errx(1, "RAND_bytes failed: %s",
591255570Strasz		    ERR_error_string(ERR_get_error(), NULL));
592255570Strasz	}
593255570Strasz
594255570Strasz	/*
595255570Strasz	 * Send the challenge.
596255570Strasz	 */
597255570Strasz	log_debugx("sending CHAP_C, binary challenge size is %zd bytes",
598255570Strasz	    sizeof(challenge_bin));
599255570Strasz	login_send_chap_c(request, id, challenge_bin,
600255570Strasz	    sizeof(challenge_bin));
601255570Strasz	pdu_delete(request);
602255570Strasz
603255570Strasz	/*
604255570Strasz	 * Receive CHAP_N/CHAP_R PDU and authenticate.
605255570Strasz	 */
606255570Strasz	log_debugx("waiting for CHAP_N/CHAP_R");
607255570Strasz	request = login_receive_chap_r(conn, ag, id, challenge_bin,
608255570Strasz	    sizeof(challenge_bin), &auth);
609255570Strasz
610255570Strasz	/*
611255570Strasz	 * Yay, authentication succeeded!
612255570Strasz	 */
613255570Strasz	log_debugx("authentication succeeded for user \"%s\"; "
614255570Strasz	    "transitioning to Negotiation Phase", auth->a_user);
615255570Strasz	login_send_chap_success(request, auth);
616255570Strasz	pdu_delete(request);
617255570Strasz}
618255570Strasz
619255570Straszstatic void
620255570Straszlogin_negotiate_key(struct pdu *request, const char *name,
621255570Strasz    const char *value, bool skipped_security, struct keys *response_keys)
622255570Strasz{
623255570Strasz	int which, tmp;
624255570Strasz	struct connection *conn;
625255570Strasz
626255570Strasz	conn = request->pdu_connection;
627255570Strasz
628255570Strasz	if (strcmp(name, "InitiatorName") == 0) {
629255570Strasz		if (!skipped_security)
630255570Strasz			log_errx(1, "initiator resent InitiatorName");
631255570Strasz	} else if (strcmp(name, "SessionType") == 0) {
632255570Strasz		if (!skipped_security)
633255570Strasz			log_errx(1, "initiator resent SessionType");
634255570Strasz	} else if (strcmp(name, "TargetName") == 0) {
635255570Strasz		if (!skipped_security)
636255570Strasz			log_errx(1, "initiator resent TargetName");
637255570Strasz	} else if (strcmp(name, "InitiatorAlias") == 0) {
638255570Strasz		if (conn->conn_initiator_alias != NULL)
639255570Strasz			free(conn->conn_initiator_alias);
640255570Strasz		conn->conn_initiator_alias = checked_strdup(value);
641255570Strasz	} else if (strcmp(value, "Irrelevant") == 0) {
642255570Strasz		/* Ignore. */
643255570Strasz	} else if (strcmp(name, "HeaderDigest") == 0) {
644255570Strasz		/*
645255570Strasz		 * We don't handle digests for discovery sessions.
646255570Strasz		 */
647255570Strasz		if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) {
648255570Strasz			log_debugx("discovery session; digests disabled");
649255570Strasz			keys_add(response_keys, name, "None");
650255570Strasz			return;
651255570Strasz		}
652255570Strasz
653255570Strasz		which = login_list_prefers(value, "CRC32C", "None");
654255570Strasz		switch (which) {
655255570Strasz		case 1:
656255570Strasz			log_debugx("initiator prefers CRC32C "
657255570Strasz			    "for header digest; we'll use it");
658255570Strasz			conn->conn_header_digest = CONN_DIGEST_CRC32C;
659255570Strasz			keys_add(response_keys, name, "CRC32C");
660255570Strasz			break;
661255570Strasz		case 2:
662255570Strasz			log_debugx("initiator prefers not to do "
663255570Strasz			    "header digest; we'll comply");
664255570Strasz			keys_add(response_keys, name, "None");
665255570Strasz			break;
666255570Strasz		default:
667255570Strasz			log_warnx("initiator sent unrecognized "
668255570Strasz			    "HeaderDigest value \"%s\"; will use None", value);
669255570Strasz			keys_add(response_keys, name, "None");
670255570Strasz			break;
671255570Strasz		}
672255570Strasz	} else if (strcmp(name, "DataDigest") == 0) {
673255570Strasz		if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) {
674255570Strasz			log_debugx("discovery session; digests disabled");
675255570Strasz			keys_add(response_keys, name, "None");
676255570Strasz			return;
677255570Strasz		}
678255570Strasz
679255570Strasz		which = login_list_prefers(value, "CRC32C", "None");
680255570Strasz		switch (which) {
681255570Strasz		case 1:
682255570Strasz			log_debugx("initiator prefers CRC32C "
683255570Strasz			    "for data digest; we'll use it");
684255570Strasz			conn->conn_data_digest = CONN_DIGEST_CRC32C;
685255570Strasz			keys_add(response_keys, name, "CRC32C");
686255570Strasz			break;
687255570Strasz		case 2:
688255570Strasz			log_debugx("initiator prefers not to do "
689255570Strasz			    "data digest; we'll comply");
690255570Strasz			keys_add(response_keys, name, "None");
691255570Strasz			break;
692255570Strasz		default:
693255570Strasz			log_warnx("initiator sent unrecognized "
694255570Strasz			    "DataDigest value \"%s\"; will use None", value);
695255570Strasz			keys_add(response_keys, name, "None");
696255570Strasz			break;
697255570Strasz		}
698255570Strasz	} else if (strcmp(name, "MaxConnections") == 0) {
699255570Strasz		keys_add(response_keys, name, "1");
700255570Strasz	} else if (strcmp(name, "InitialR2T") == 0) {
701255570Strasz		keys_add(response_keys, name, "Yes");
702255570Strasz	} else if (strcmp(name, "ImmediateData") == 0) {
703255570Strasz		if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) {
704255570Strasz			log_debugx("discovery session; ImmediateData irrelevant");
705255570Strasz			keys_add(response_keys, name, "Irrelevant");
706255570Strasz		} else {
707255570Strasz			if (strcmp(value, "Yes") == 0) {
708255570Strasz				conn->conn_immediate_data = true;
709255570Strasz				keys_add(response_keys, name, "Yes");
710255570Strasz			} else {
711255570Strasz				conn->conn_immediate_data = false;
712255570Strasz				keys_add(response_keys, name, "No");
713255570Strasz			}
714255570Strasz		}
715255570Strasz	} else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) {
716255570Strasz		tmp = strtoul(value, NULL, 10);
717255570Strasz		if (tmp <= 0) {
718255570Strasz			login_send_error(request, 0x02, 0x00);
719255570Strasz			log_errx(1, "received invalid "
720255570Strasz			    "MaxRecvDataSegmentLength");
721255570Strasz		}
722255570Strasz		if (tmp > MAX_DATA_SEGMENT_LENGTH) {
723271626Strasz			log_debugx("capping MaxRecvDataSegmentLength "
724271626Strasz			    "from %d to %d", tmp, MAX_DATA_SEGMENT_LENGTH);
725255570Strasz			tmp = MAX_DATA_SEGMENT_LENGTH;
726255570Strasz		}
727255570Strasz		conn->conn_max_data_segment_length = tmp;
728255570Strasz		keys_add_int(response_keys, name, tmp);
729255570Strasz	} else if (strcmp(name, "MaxBurstLength") == 0) {
730255570Strasz		tmp = strtoul(value, NULL, 10);
731255570Strasz		if (tmp <= 0) {
732255570Strasz			login_send_error(request, 0x02, 0x00);
733255570Strasz			log_errx(1, "received invalid MaxBurstLength");
734255570Strasz		}
735255570Strasz		if (tmp > MAX_BURST_LENGTH) {
736255570Strasz			log_debugx("capping MaxBurstLength from %d to %d",
737255570Strasz			    tmp, MAX_BURST_LENGTH);
738255570Strasz			tmp = MAX_BURST_LENGTH;
739255570Strasz		}
740255570Strasz		conn->conn_max_burst_length = tmp;
741255570Strasz		keys_add(response_keys, name, value);
742255570Strasz	} else if (strcmp(name, "FirstBurstLength") == 0) {
743255570Strasz		tmp = strtoul(value, NULL, 10);
744255570Strasz		if (tmp <= 0) {
745255570Strasz			login_send_error(request, 0x02, 0x00);
746255570Strasz			log_errx(1, "received invalid "
747255570Strasz			    "FirstBurstLength");
748255570Strasz		}
749255570Strasz		if (tmp > MAX_DATA_SEGMENT_LENGTH) {
750255570Strasz			log_debugx("capping FirstBurstLength from %d to %d",
751255570Strasz			    tmp, MAX_DATA_SEGMENT_LENGTH);
752255570Strasz			tmp = MAX_DATA_SEGMENT_LENGTH;
753255570Strasz		}
754255570Strasz		/*
755255570Strasz		 * We don't pass the value to the kernel; it only enforces
756255570Strasz		 * hardcoded limit anyway.
757255570Strasz		 */
758255570Strasz		keys_add_int(response_keys, name, tmp);
759255570Strasz	} else if (strcmp(name, "DefaultTime2Wait") == 0) {
760255570Strasz		keys_add(response_keys, name, value);
761255570Strasz	} else if (strcmp(name, "DefaultTime2Retain") == 0) {
762255570Strasz		keys_add(response_keys, name, "0");
763255570Strasz	} else if (strcmp(name, "MaxOutstandingR2T") == 0) {
764255570Strasz		keys_add(response_keys, name, "1");
765255570Strasz	} else if (strcmp(name, "DataPDUInOrder") == 0) {
766255570Strasz		keys_add(response_keys, name, "Yes");
767255570Strasz	} else if (strcmp(name, "DataSequenceInOrder") == 0) {
768255570Strasz		keys_add(response_keys, name, "Yes");
769255570Strasz	} else if (strcmp(name, "ErrorRecoveryLevel") == 0) {
770255570Strasz		keys_add(response_keys, name, "0");
771255570Strasz	} else if (strcmp(name, "OFMarker") == 0) {
772255570Strasz		keys_add(response_keys, name, "No");
773255570Strasz	} else if (strcmp(name, "IFMarker") == 0) {
774255570Strasz		keys_add(response_keys, name, "No");
775255570Strasz	} else {
776255570Strasz		log_debugx("unknown key \"%s\"; responding "
777255570Strasz		    "with NotUnderstood", name);
778255570Strasz		keys_add(response_keys, name, "NotUnderstood");
779255570Strasz	}
780255570Strasz}
781255570Strasz
782255570Straszstatic void
783255570Straszlogin_negotiate(struct connection *conn, struct pdu *request)
784255570Strasz{
785255570Strasz	struct pdu *response;
786255570Strasz	struct iscsi_bhs_login_response *bhslr2;
787255570Strasz	struct keys *request_keys, *response_keys;
788271701Strasz	char *portal_group_tag;
789271701Strasz	int i, rv;
790255570Strasz	bool skipped_security;
791255570Strasz
792255570Strasz	if (request == NULL) {
793265515Strasz		log_debugx("beginning operational parameter negotiation; "
794255570Strasz		    "waiting for Login PDU");
795255570Strasz		request = login_receive(conn, false);
796255570Strasz		skipped_security = false;
797255570Strasz	} else
798255570Strasz		skipped_security = true;
799255570Strasz
800255570Strasz	request_keys = keys_new();
801255570Strasz	keys_load(request_keys, request);
802255570Strasz
803255570Strasz	response = login_new_response(request);
804255570Strasz	bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
805255570Strasz	bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT;
806255570Strasz	bhslr2->bhslr_tsih = htons(0xbadd);
807255570Strasz	login_set_csg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
808255570Strasz	login_set_nsg(response, BHSLR_STAGE_FULL_FEATURE_PHASE);
809255570Strasz	response_keys = keys_new();
810271701Strasz
811271701Strasz	if (skipped_security &&
812271701Strasz	    conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
813271701Strasz		if (conn->conn_target->t_alias != NULL)
814271701Strasz			keys_add(response_keys,
815271701Strasz			    "TargetAlias", conn->conn_target->t_alias);
816271701Strasz		rv = asprintf(&portal_group_tag, "%d",
817271701Strasz		    conn->conn_portal->p_portal_group->pg_tag);
818271701Strasz		if (rv <= 0)
819271701Strasz			log_err(1, "asprintf");
820271701Strasz		keys_add(response_keys,
821271701Strasz		    "TargetPortalGroupTag", portal_group_tag);
822271701Strasz		free(portal_group_tag);
823271701Strasz	}
824271701Strasz
825255570Strasz	for (i = 0; i < KEYS_MAX; i++) {
826255570Strasz		if (request_keys->keys_names[i] == NULL)
827255570Strasz			break;
828255570Strasz
829255570Strasz		login_negotiate_key(request, request_keys->keys_names[i],
830255570Strasz		    request_keys->keys_values[i], skipped_security,
831255570Strasz		    response_keys);
832255570Strasz	}
833255570Strasz
834265515Strasz	log_debugx("operational parameter negotiation done; "
835255570Strasz	    "transitioning to Full Feature Phase");
836255570Strasz
837255570Strasz	keys_save(response_keys, response);
838255570Strasz	pdu_send(response);
839255570Strasz	pdu_delete(response);
840255570Strasz	keys_delete(response_keys);
841255570Strasz	pdu_delete(request);
842255570Strasz	keys_delete(request_keys);
843255570Strasz}
844255570Strasz
845255570Straszvoid
846255570Straszlogin(struct connection *conn)
847255570Strasz{
848255570Strasz	struct pdu *request, *response;
849255570Strasz	struct iscsi_bhs_login_request *bhslr;
850255570Strasz	struct iscsi_bhs_login_response *bhslr2;
851255570Strasz	struct keys *request_keys, *response_keys;
852255570Strasz	struct auth_group *ag;
853255570Strasz	const char *initiator_name, *initiator_alias, *session_type,
854255570Strasz	    *target_name, *auth_method;
855255570Strasz	char *portal_group_tag;
856255570Strasz	int rv;
857255570Strasz
858255570Strasz	/*
859255570Strasz	 * Handle the initial Login Request - figure out required authentication
860255570Strasz	 * method and either transition to the next phase, if no authentication
861255570Strasz	 * is required, or call appropriate authentication code.
862255570Strasz	 */
863255570Strasz	log_debugx("beginning Login Phase; waiting for Login PDU");
864255570Strasz	request = login_receive(conn, true);
865255570Strasz	bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs;
866255570Strasz	if (bhslr->bhslr_tsih != 0) {
867255570Strasz		login_send_error(request, 0x02, 0x0a);
868255570Strasz		log_errx(1, "received Login PDU with non-zero TSIH");
869255570Strasz	}
870255570Strasz
871268684Smav	memcpy(conn->conn_initiator_isid, bhslr->bhslr_isid,
872268684Smav	    sizeof(conn->conn_initiator_isid));
873268684Smav
874255570Strasz	/*
875255570Strasz	 * XXX: Implement the C flag some day.
876255570Strasz	 */
877255570Strasz	request_keys = keys_new();
878255570Strasz	keys_load(request_keys, request);
879255570Strasz
880255570Strasz	assert(conn->conn_initiator_name == NULL);
881255570Strasz	initiator_name = keys_find(request_keys, "InitiatorName");
882255570Strasz	if (initiator_name == NULL) {
883255570Strasz		login_send_error(request, 0x02, 0x07);
884255570Strasz		log_errx(1, "received Login PDU without InitiatorName");
885255570Strasz	}
886255570Strasz	if (valid_iscsi_name(initiator_name) == false) {
887255570Strasz		login_send_error(request, 0x02, 0x00);
888255570Strasz		log_errx(1, "received Login PDU with invalid InitiatorName");
889255570Strasz	}
890255570Strasz	conn->conn_initiator_name = checked_strdup(initiator_name);
891255570Strasz	log_set_peer_name(conn->conn_initiator_name);
892255570Strasz	/*
893255570Strasz	 * XXX: This doesn't work (does nothing) because of Capsicum.
894255570Strasz	 */
895255570Strasz	setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name);
896255570Strasz
897255570Strasz	initiator_alias = keys_find(request_keys, "InitiatorAlias");
898255570Strasz	if (initiator_alias != NULL)
899255570Strasz		conn->conn_initiator_alias = checked_strdup(initiator_alias);
900255570Strasz
901255570Strasz	assert(conn->conn_session_type == CONN_SESSION_TYPE_NONE);
902255570Strasz	session_type = keys_find(request_keys, "SessionType");
903255570Strasz	if (session_type != NULL) {
904255570Strasz		if (strcmp(session_type, "Normal") == 0) {
905255570Strasz			conn->conn_session_type = CONN_SESSION_TYPE_NORMAL;
906255570Strasz		} else if (strcmp(session_type, "Discovery") == 0) {
907255570Strasz			conn->conn_session_type = CONN_SESSION_TYPE_DISCOVERY;
908255570Strasz		} else {
909255570Strasz			login_send_error(request, 0x02, 0x00);
910255570Strasz			log_errx(1, "received Login PDU with invalid "
911255570Strasz			    "SessionType \"%s\"", session_type);
912255570Strasz		}
913255570Strasz	} else
914255570Strasz		conn->conn_session_type = CONN_SESSION_TYPE_NORMAL;
915255570Strasz
916255570Strasz	assert(conn->conn_target == NULL);
917255570Strasz	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
918255570Strasz		target_name = keys_find(request_keys, "TargetName");
919255570Strasz		if (target_name == NULL) {
920255570Strasz			login_send_error(request, 0x02, 0x07);
921255570Strasz			log_errx(1, "received Login PDU without TargetName");
922255570Strasz		}
923255570Strasz
924255570Strasz		conn->conn_target =
925255570Strasz		    target_find(conn->conn_portal->p_portal_group->pg_conf,
926255570Strasz		    target_name);
927255570Strasz		if (conn->conn_target == NULL) {
928255570Strasz			login_send_error(request, 0x02, 0x03);
929255570Strasz			log_errx(1, "requested target \"%s\" not found",
930255570Strasz			    target_name);
931255570Strasz		}
932255570Strasz	}
933255570Strasz
934255570Strasz	/*
935255570Strasz	 * At this point we know what kind of authentication we need.
936255570Strasz	 */
937255570Strasz	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
938255570Strasz		ag = conn->conn_target->t_auth_group;
939255570Strasz		if (ag->ag_name != NULL) {
940255570Strasz			log_debugx("initiator requests to connect "
941255570Strasz			    "to target \"%s\"; auth-group \"%s\"",
942263723Strasz			    conn->conn_target->t_name,
943255570Strasz			    conn->conn_target->t_auth_group->ag_name);
944255570Strasz		} else {
945255570Strasz			log_debugx("initiator requests to connect "
946263723Strasz			    "to target \"%s\"", conn->conn_target->t_name);
947255570Strasz		}
948255570Strasz	} else {
949255570Strasz		assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY);
950255570Strasz		ag = conn->conn_portal->p_portal_group->pg_discovery_auth_group;
951255570Strasz		if (ag->ag_name != NULL) {
952255570Strasz			log_debugx("initiator requests "
953255570Strasz			    "discovery session; auth-group \"%s\"", ag->ag_name);
954255570Strasz		} else {
955255570Strasz			log_debugx("initiator requests discovery session");
956255570Strasz		}
957255570Strasz	}
958255570Strasz
959255570Strasz	/*
960263720Strasz	 * Enforce initiator-name and initiator-portal.
961263720Strasz	 */
962263720Strasz	if (auth_name_defined(ag)) {
963263720Strasz		if (auth_name_find(ag, initiator_name) == NULL) {
964263720Strasz			login_send_error(request, 0x02, 0x02);
965263720Strasz			log_errx(1, "initiator does not match allowed "
966263720Strasz			    "initiator names");
967263720Strasz		}
968263720Strasz		log_debugx("initiator matches allowed initiator names");
969263720Strasz	} else {
970263720Strasz		log_debugx("auth-group does not define initiator name "
971263720Strasz		    "restrictions");
972263720Strasz	}
973263720Strasz
974263720Strasz	if (auth_portal_defined(ag)) {
975270137Smav		if (auth_portal_find(ag, &conn->conn_initiator_sa) == NULL) {
976263720Strasz			login_send_error(request, 0x02, 0x02);
977263720Strasz			log_errx(1, "initiator does not match allowed "
978263720Strasz			    "initiator portals");
979263720Strasz		}
980263720Strasz		log_debugx("initiator matches allowed initiator portals");
981263720Strasz	} else {
982263720Strasz		log_debugx("auth-group does not define initiator portal "
983263720Strasz		    "restrictions");
984263720Strasz	}
985263720Strasz
986263720Strasz	/*
987255570Strasz	 * Let's see if the initiator intends to do any kind of authentication
988255570Strasz	 * at all.
989255570Strasz	 */
990255570Strasz	if (login_csg(request) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) {
991255570Strasz		if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) {
992255570Strasz			login_send_error(request, 0x02, 0x01);
993255570Strasz			log_errx(1, "initiator skipped the authentication, "
994255570Strasz			    "but authentication is required");
995255570Strasz		}
996255570Strasz
997255570Strasz		keys_delete(request_keys);
998255570Strasz
999255570Strasz		log_debugx("initiator skipped the authentication, "
1000255570Strasz		    "and we don't need it; proceeding with negotiation");
1001255570Strasz		login_negotiate(conn, request);
1002255570Strasz		return;
1003255570Strasz	}
1004255570Strasz
1005255570Strasz	if (ag->ag_type == AG_TYPE_NO_AUTHENTICATION) {
1006255570Strasz		/*
1007255570Strasz		 * Initiator might want to to authenticate,
1008255570Strasz		 * but we don't need it.
1009255570Strasz		 */
1010255570Strasz		log_debugx("authentication not required; "
1011265515Strasz		    "transitioning to operational parameter negotiation");
1012255570Strasz
1013255570Strasz		if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0)
1014255570Strasz			log_warnx("initiator did not set the \"T\" flag; "
1015255570Strasz			    "transitioning anyway");
1016255570Strasz
1017255570Strasz		response = login_new_response(request);
1018255570Strasz		bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs;
1019255570Strasz		bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT;
1020255570Strasz		login_set_nsg(response,
1021255570Strasz		    BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
1022255570Strasz		response_keys = keys_new();
1023255570Strasz		/*
1024255570Strasz		 * Required by Linux initiator.
1025255570Strasz		 */
1026255570Strasz		auth_method = keys_find(request_keys, "AuthMethod");
1027255570Strasz		if (auth_method != NULL &&
1028255570Strasz		    login_list_contains(auth_method, "None"))
1029255570Strasz			keys_add(response_keys, "AuthMethod", "None");
1030255570Strasz
1031255570Strasz		if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
1032255570Strasz			if (conn->conn_target->t_alias != NULL)
1033255570Strasz				keys_add(response_keys,
1034255570Strasz				    "TargetAlias", conn->conn_target->t_alias);
1035255570Strasz			rv = asprintf(&portal_group_tag, "%d",
1036255570Strasz			    conn->conn_portal->p_portal_group->pg_tag);
1037255570Strasz			if (rv <= 0)
1038255570Strasz				log_err(1, "asprintf");
1039255570Strasz			keys_add(response_keys,
1040255570Strasz			    "TargetPortalGroupTag", portal_group_tag);
1041255570Strasz			free(portal_group_tag);
1042255570Strasz		}
1043255570Strasz		keys_save(response_keys, response);
1044255570Strasz		pdu_send(response);
1045255570Strasz		pdu_delete(response);
1046255570Strasz		keys_delete(response_keys);
1047255570Strasz		pdu_delete(request);
1048255570Strasz		keys_delete(request_keys);
1049255570Strasz
1050255570Strasz		login_negotiate(conn, NULL);
1051255570Strasz		return;
1052255570Strasz	}
1053255570Strasz
1054263729Strasz	if (ag->ag_type == AG_TYPE_DENY) {
1055263729Strasz		login_send_error(request, 0x02, 0x01);
1056263734Strasz		log_errx(1, "auth-type is \"deny\"");
1057263729Strasz	}
1058263729Strasz
1059259305Strasz	if (ag->ag_type == AG_TYPE_UNKNOWN) {
1060259305Strasz		/*
1061259305Strasz		 * This can happen with empty auth-group.
1062259305Strasz		 */
1063259305Strasz		login_send_error(request, 0x02, 0x01);
1064263734Strasz		log_errx(1, "auth-type not set, denying access");
1065259305Strasz	}
1066259305Strasz
1067255570Strasz	log_debugx("CHAP authentication required");
1068255570Strasz
1069255570Strasz	auth_method = keys_find(request_keys, "AuthMethod");
1070255570Strasz	if (auth_method == NULL) {
1071255570Strasz		login_send_error(request, 0x02, 0x07);
1072255570Strasz		log_errx(1, "received Login PDU without AuthMethod");
1073255570Strasz	}
1074255570Strasz	/*
1075255570Strasz	 * XXX: This should be Reject, not just a login failure (5.3.2).
1076255570Strasz	 */
1077255570Strasz	if (login_list_contains(auth_method, "CHAP") == 0) {
1078255570Strasz		login_send_error(request, 0x02, 0x01);
1079255570Strasz		log_errx(1, "initiator requests unsupported AuthMethod \"%s\" "
1080255570Strasz		    "instead of \"CHAP\"", auth_method);
1081255570Strasz	}
1082255570Strasz
1083255570Strasz	response = login_new_response(request);
1084255570Strasz
1085255570Strasz	response_keys = keys_new();
1086255570Strasz	keys_add(response_keys, "AuthMethod", "CHAP");
1087255570Strasz	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
1088271701Strasz		if (conn->conn_target->t_alias != NULL)
1089271701Strasz			keys_add(response_keys,
1090271701Strasz			    "TargetAlias", conn->conn_target->t_alias);
1091255570Strasz		rv = asprintf(&portal_group_tag, "%d",
1092255570Strasz		    conn->conn_portal->p_portal_group->pg_tag);
1093255570Strasz		if (rv <= 0)
1094255570Strasz			log_err(1, "asprintf");
1095255570Strasz		keys_add(response_keys,
1096255570Strasz		    "TargetPortalGroupTag", portal_group_tag);
1097255570Strasz		free(portal_group_tag);
1098255570Strasz	}
1099255570Strasz	keys_save(response_keys, response);
1100255570Strasz
1101255570Strasz	pdu_send(response);
1102255570Strasz	pdu_delete(response);
1103255570Strasz	keys_delete(response_keys);
1104255570Strasz	pdu_delete(request);
1105255570Strasz	keys_delete(request_keys);
1106255570Strasz
1107255570Strasz	login_chap(conn, ag);
1108255570Strasz
1109255570Strasz	login_negotiate(conn, NULL);
1110255570Strasz}
1111