2 * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 | $Id: login.c,v 1.4 2007/04/27 07:40:40 danny Exp danny $
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/sysctl.h>
39 #include <netinet/in.h>
40 #include <netinet/tcp.h>
41 #include <arpa/inet.h>
42 #if __FreeBSD_version < 500000
45 #include <sys/ioctl.h>
50 #include <dev/iscsi/initiator/iscsi.h>
51 #include "iscontrol.h"
53 static char *status_class1[] = {
55 "Authentication failure",
56 "Authorization failure",
59 "Unsupported version",
60 "Too many connections",
62 "Can't include in session",
63 "Session type not suported",
64 "Session does not exist",
65 "Invalid during login",
67 #define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
69 static char *status_class3[] = {
71 "Service unavailable",
74 #define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
77 selectFrom(char *str, token_t *list)
85 sep = strchr(sp, ',');
91 for(lp = list; lp->name != NULL; lp++) {
92 if(strncasecmp(lp->name, sp, n) == 0)
93 return strdup(lp->name);
102 getkeyval(char *key, pdu_t *pp)
110 ptr = (char *)pp->ds_addr;
113 if(strncmp(key, ptr, klen) == 0)
123 handleTgtResp(isess_t *sess, pdu_t *pp)
125 isc_opt_t *op = sess->op;
126 char *np, *rp, *d1, *d2;
130 if(((np = getkeyval("CHAP_N=", pp)) == NULL) ||
131 ((rp = getkeyval("CHAP_R=", pp)) == NULL))
133 if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) {
134 fprintf(stderr, "%s does not match\n", np);
137 l1 = str2bin(op->tgtChapDigest, &d1);
138 l2 = str2bin(rp, &d2);
140 debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp);
141 if(l1 == l2 && memcmp(d1, d2, l1) == 0)
148 free(op->tgtChapDigest);
149 op->tgtChapDigest = NULL;
151 debug(3, "res=%d", res);
157 processParams(isess_t *sess, pdu_t *pp)
159 isc_opt_t *op = sess->op;
166 ptr = (char *)pp->ds_addr;
169 printf("got: len=%d %s\n", len, ptr);
171 if((eq = strchr(ptr, '=')) != NULL)
174 if(strncmp(ptr, "TargetAddress", klen) == 0) {
175 char *p, *q, *ta = NULL;
177 // TargetAddress=domainname[:port][,portal-group-tag]
178 // XXX: if(op->targetAddress) free(op->targetAddress);
179 q = op->targetAddress = strdup(eq+1);
182 if((q = strchr(q, ']')) != NULL) {
184 ta = op->targetAddress;
185 op->targetAddress = strdup(ta+1);
187 q = op->targetAddress;
189 if((p = strchr(q, ',')) != NULL) {
191 op->targetPortalGroupTag = atoi(p);
193 if((p = strchr(q, ':')) != NULL) {
199 } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) {
201 op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0);
202 } else if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) {
203 op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0);
204 } else if(strncmp(ptr, "HeaderDigest", klen) == 0) {
205 op->headerDigest = selectFrom(eq+1, DigestMethods);
206 } else if(strncmp(ptr, "DataDigest", klen) == 0) {
207 op->dataDigest = selectFrom(eq+1, DigestMethods);
208 } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0)
209 op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0);
212 for(kp = keyMap; kp->name; kp++) {
213 if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=')
214 mp->func(sess, ptr+kp->len+1, GET);
226 handleLoginResp(isess_t *sess, pdu_t *pp)
228 login_rsp_t *lp = (login_rsp_t *)pp;
229 uint st_class, status = ntohs(lp->status);
232 debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status);
234 st_class = status >> 8;
236 uint st_detail = status & 0xff;
241 // the ITN (iSCSI target Name) requests a:
242 case 1: // temporary address change
243 case 2: // permanent address change
248 case 2: // Initiator Error
249 if(st_detail < CLASS1_ERRS)
250 printf("0x%04x: %s\n", status, status_class1[st_detail]);
254 if(st_detail < CLASS3_ERRS)
255 printf("0x%04x: %s\n", status, status_class3[st_detail]);
261 processParams(sess, pp);
262 setOptions(sess, 0); // XXX: just in case ...
265 isc_opt_t *op = sess->op;
267 if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL))
268 if(handleTgtResp(sess, pp) != 0)
269 return 1; // XXX: Authentication failure ...
271 if(sess->csg == FF_PHASE) {
272 // XXX: will need this when implementing reconnect.
273 sess->tsih = lp->tsih;
274 debug(2, "TSIH=%x", sess->tsih);
283 handleChap(isess_t *sess, pdu_t *pp)
287 isc_opt_t *op = sess->op;
288 char *ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits
292 bzero(&spp, sizeof(pdu_t));
293 lp = (login_req_t *)&spp.ipdu.bhs;
294 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
295 memcpy(lp->isid, sess->isid, 6);
296 lp->tsih = sess->tsih; // MUST be zero the first time!
298 lp->CSG = SN_PHASE; // Security Negotiation
302 if(((ap = getkeyval("CHAP_A=", pp)) == NULL) ||
303 ((ip = getkeyval("CHAP_I=", pp)) == NULL) ||
304 ((cp = getkeyval("CHAP_C=", pp)) == NULL))
307 if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL)
310 addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName);
311 addText(&spp, "CHAP_R=%s", digest);
314 if(op->tgtChapSecret != NULL) {
315 op->tgtChapID = (random() >> 24) % 255; // should be random enough ...
316 addText(&spp, "CHAP_I=%d", op->tgtChapID);
317 cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8);
318 addText(&spp, "CHAP_C=%s", cp);
319 op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret);
322 return sendPDU(sess, &spp, handleLoginResp);
326 authenticate(isess_t *sess)
330 isc_opt_t *op = sess->op;
332 bzero(&spp, sizeof(pdu_t));
333 lp = (login_req_t *)&spp.ipdu.bhs;
334 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
335 memcpy(lp->isid, sess->isid, 6);
336 lp->tsih = sess->tsih; // MUST be zero the first time!
338 lp->CSG = SN_PHASE; // Security Negotiation
342 switch((authm_t)lookup(AuthMethods, op->authMethod)) {
353 if(op->chapDigest == 0)
354 addText(&spp, "CHAP_A=5");
356 if(strcmp(op->chapDigest, "MD5") == 0)
357 addText(&spp, "CHAP_A=5");
359 if(strcmp(op->chapDigest, "SHA1") == 0)
360 addText(&spp, "CHAP_A=7");
362 addText(&spp, "CHAP_A=5,7");
363 return sendPDU(sess, &spp, handleChap);
369 loginPhase(isess_t *sess)
371 pdu_t spp, *sp = &spp;
372 isc_opt_t *op = sess->op;
378 bzero(sp, sizeof(pdu_t));
379 lp = (login_req_t *)&spp.ipdu.bhs;
380 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
381 memcpy(lp->isid, sess->isid, 6);
382 lp->tsih = sess->tsih; // MUST be zero the first time!
383 lp->CID = htons(1); // sess->cid?
385 if((lp->CSG = sess->csg) == LON_PHASE)
386 lp->NSG = FF_PHASE; // lets try and go full feature ...
389 lp->T = 1; // transit to next login stage
391 if(sess->flags & SESS_INITIALLOGIN1) {
392 sess->flags &= ~SESS_INITIALLOGIN1;
394 addText(sp, "SessionType=%s", op->sessionType);
395 addText(sp, "InitiatorName=%s", op->initiatorName);
396 if(strcmp(op->sessionType, "Discovery") != 0) {
397 addText(sp, "TargetName=%s", op->targetName);
401 case SN_PHASE: // Security Negotiation
402 addText(sp, "AuthMethod=%s", op->authMethod);
405 case LON_PHASE: // Login Operational Negotiation
406 if((sess->flags & SESS_NEGODONE) == 0) {
407 sess->flags |= SESS_NEGODONE;
408 addText(sp, "MaxBurstLength=%d", op->maxBurstLength);
409 addText(sp, "HeaderDigest=%s", op->headerDigest);
410 addText(sp, "DataDigest=%s", op->dataDigest);
411 addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength);
412 addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel);
413 addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait);
414 addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain);
415 addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No");
416 addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No");
417 addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T);
419 if(strcmp(op->sessionType, "Discovery") != 0) {
420 addText(sp, "MaxConnections=%d", op->maxConnections);
421 addText(sp, "FirstBurstLength=%d", op->firstBurstLength);
422 addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No");
423 addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No");
430 status = sendPDU(sess, &spp, handleLoginResp);
433 case 0: // all is ok ...
434 if(sess->csg == SN_PHASE)
436 | if we are still here, then we need
437 | to exchange some secrets ...
439 status = authenticate(sess);