2 * Copyright (c) 2005-2008 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>
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/sysctl.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
40 #include <arpa/inet.h>
41 #if __FreeBSD_version < 500000
44 #include <sys/ioctl.h>
50 #include "iscontrol.h"
52 static char *status_class1
[] = {
54 "Authentication failure",
55 "Authorization failure",
58 "Unsupported version",
59 "Too many connections",
61 "Can't include in session",
62 "Session type not suported",
63 "Session does not exist",
64 "Invalid during login",
66 #define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
68 static char *status_class3
[] = {
70 "Service unavailable",
73 #define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
76 selectFrom(char *str
, token_t
*list
)
84 sep
= strchr(sp
, ',');
90 for(lp
= list
; lp
->name
!= NULL
; lp
++) {
91 if(strncasecmp(lp
->name
, sp
, n
) == 0)
92 return strdup(lp
->name
);
101 getkeyval(char *key
, pdu_t
*pp
)
109 ptr
= (char *)pp
->ds
;
112 if(strncmp(key
, ptr
, klen
) == 0)
122 handleTgtResp(isess_t
*sess
, pdu_t
*pp
)
124 isc_opt_t
*op
= sess
->op
;
125 char *np
, *rp
, *d1
, *d2
;
129 if(((np
= getkeyval("CHAP_N=", pp
)) == NULL
) ||
130 ((rp
= getkeyval("CHAP_R=", pp
)) == NULL
))
132 if(strcmp(np
, op
->tgtChapName
? op
->tgtChapName
: op
->initiatorName
) != 0) {
133 fprintf(stderr
, "%s does not match\n", np
);
136 l1
= str2bin(op
->tgtChapDigest
, &d1
);
137 l2
= str2bin(rp
, &d2
);
139 debug(3, "l1=%d '%s' l2=%d '%s'", l1
, op
->tgtChapDigest
, l2
, rp
);
140 if(l1
== l2
&& memcmp(d1
, d2
, l1
) == 0)
147 free(op
->tgtChapDigest
);
148 op
->tgtChapDigest
= NULL
;
150 debug(3, "res=%d", res
);
156 processParams(isess_t
*sess
, pdu_t
*pp
)
158 isc_opt_t
*op
= sess
->op
;
165 ptr
= (char *)pp
->ds
;
168 printf("got: len=%d %s\n", len
, ptr
);
170 if((eq
= strchr(ptr
, '=')) != NULL
)
173 if(strncmp(ptr
, "TargetAddress", klen
) == 0) {
174 char *p
, *q
, *ta
= NULL
;
176 // TargetAddress=domainname[:port][,portal-group-tag]
177 // XXX: if(op->targetAddress) free(op->targetAddress);
178 q
= op
->targetAddress
= strdup(eq
+1);
181 if((q
= strchr(q
, ']')) != NULL
) {
183 ta
= op
->targetAddress
;
184 op
->targetAddress
= strdup(ta
+1);
186 q
= op
->targetAddress
;
188 if((p
= strchr(q
, ',')) != NULL
) {
190 op
->targetPortalGroupTag
= atoi(p
);
192 if((p
= strchr(q
, ':')) != NULL
) {
198 } else if(strncmp(ptr
, "MaxRecvDataSegmentLength", klen
) == 0) {
200 op
->maxXmitDataSegmentLength
= strtol(eq
+1, (char **)NULL
, 0);
201 } else if(strncmp(ptr
, "TargetPortalGroupTag", klen
) == 0) {
202 op
->targetPortalGroupTag
= strtol(eq
+1, (char **)NULL
, 0);
203 } else if(strncmp(ptr
, "HeaderDigest", klen
) == 0) {
204 op
->headerDigest
= selectFrom(eq
+1, DigestMethods
);
205 } else if(strncmp(ptr
, "DataDigest", klen
) == 0) {
206 op
->dataDigest
= selectFrom(eq
+1, DigestMethods
);
207 } else if(strncmp(ptr
, "MaxOutstandingR2T", klen
) == 0)
208 op
->maxOutstandingR2T
= strtol(eq
+1, (char **)NULL
, 0);
211 for(kp
= keyMap
; kp
->name
; kp
++) {
212 if(strncmp(ptr
, kp
->name
, kp
->len
) == 0 && ptr
[kp
->len
] == '=')
213 mp
->func(sess
, ptr
+kp
->len
+1, GET
);
225 handleLoginResp(isess_t
*sess
, pdu_t
*pp
)
227 login_rsp_t
*lp
= (login_rsp_t
*)pp
;
228 uint st_class
, status
= ntohs(lp
->status
);
231 debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp
->T
, lp
->CSG
, lp
->NSG
, status
);
233 st_class
= status
>> 8;
235 unsigned int st_detail
= status
& 0xff;
240 // the ITN (iSCSI target Name) requests a:
241 case 1: // temporary address change
242 case 2: // permanent address change
247 case 2: // Initiator Error
248 if(st_detail
< CLASS1_ERRS
)
249 printf("0x%04x: %s\n", status
, status_class1
[st_detail
]);
253 if(st_detail
< CLASS3_ERRS
)
254 printf("0x%04x: %s\n", status
, status_class3
[st_detail
]);
260 processParams(sess
, pp
);
261 setOptions(sess
, 0); // XXX: just in case ...
264 isc_opt_t
*op
= sess
->op
;
266 if(sess
->csg
== SN_PHASE
&& (op
->tgtChapDigest
!= NULL
))
267 if(handleTgtResp(sess
, pp
) != 0)
268 return 1; // XXX: Authentication failure ...
270 if(sess
->csg
== FF_PHASE
) {
271 // XXX: will need this when implementing reconnect.
272 sess
->tsih
= lp
->tsih
;
273 debug(2, "TSIH=%x", sess
->tsih
);
282 handleChap(isess_t
*sess
, pdu_t
*pp
)
286 isc_opt_t
*op
= sess
->op
;
287 char *ap
, *ip
, *cp
, *digest
; // MD5 is 128bits, SHA1 160bits
291 bzero(&spp
, sizeof(pdu_t
));
292 lp
= (login_req_t
*)&spp
.ipdu
.bhs
;
293 lp
->cmd
= ISCSI_LOGIN_CMD
| 0x40; // login request + Inmediate
294 memcpy(lp
->isid
, sess
->isid
, 6);
295 lp
->tsih
= sess
->tsih
; // MUST be zero the first time!
297 lp
->CSG
= SN_PHASE
; // Security Negotiation
301 if(((ap
= getkeyval("CHAP_A=", pp
)) == NULL
) ||
302 ((ip
= getkeyval("CHAP_I=", pp
)) == NULL
) ||
303 ((cp
= getkeyval("CHAP_C=", pp
)) == NULL
))
306 if((digest
= chapDigest(ap
, (char)strtol(ip
, (char **)NULL
, 0), cp
, op
->chapSecret
)) == NULL
)
309 addText(&spp
, "CHAP_N=%s", op
->chapIName
? op
->chapIName
: op
->initiatorName
);
310 addText(&spp
, "CHAP_R=%s", digest
);
313 if(op
->tgtChapSecret
!= NULL
) {
314 op
->tgtChapID
= (random() >> 24) % 255; // should be random enough ...
315 addText(&spp
, "CHAP_I=%d", op
->tgtChapID
);
316 cp
= genChapChallenge(cp
, op
->tgtChallengeLen
? op
->tgtChallengeLen
: 8);
317 addText(&spp
, "CHAP_C=%s", cp
);
318 op
->tgtChapDigest
= chapDigest(ap
, op
->tgtChapID
, cp
, op
->tgtChapSecret
);
321 return sendPDU(sess
, &spp
, handleLoginResp
);
325 authenticate(isess_t
*sess
)
329 isc_opt_t
*op
= sess
->op
;
331 bzero(&spp
, sizeof(pdu_t
));
332 lp
= (login_req_t
*)&spp
.ipdu
.bhs
;
333 lp
->cmd
= ISCSI_LOGIN_CMD
| 0x40; // login request + Inmediate
334 memcpy(lp
->isid
, sess
->isid
, 6);
335 lp
->tsih
= sess
->tsih
; // MUST be zero the first time!
337 lp
->CSG
= SN_PHASE
; // Security Negotiation
341 switch((authm_t
)lookup(AuthMethods
, op
->authMethod
)) {
352 if(op
->chapDigest
== 0)
353 addText(&spp
, "CHAP_A=5");
355 if(strcmp(op
->chapDigest
, "MD5") == 0)
356 addText(&spp
, "CHAP_A=5");
358 if(strcmp(op
->chapDigest
, "SHA1") == 0)
359 addText(&spp
, "CHAP_A=7");
361 addText(&spp
, "CHAP_A=5,7");
362 return sendPDU(sess
, &spp
, handleChap
);
368 loginPhase(isess_t
*sess
)
370 pdu_t spp
, *sp
= &spp
;
371 isc_opt_t
*op
= sess
->op
;
377 bzero(sp
, sizeof(pdu_t
));
378 lp
= (login_req_t
*)&spp
.ipdu
.bhs
;
379 lp
->cmd
= ISCSI_LOGIN_CMD
| 0x40; // login request + Inmediate
380 memcpy(lp
->isid
, sess
->isid
, 6);
381 lp
->tsih
= sess
->tsih
; // MUST be zero the first time!
382 lp
->CID
= htons(1); // sess->cid?
384 if((lp
->CSG
= sess
->csg
) == LON_PHASE
)
385 lp
->NSG
= FF_PHASE
; // lets try and go full feature ...
388 lp
->T
= 1; // transit to next login stage
390 if(sess
->flags
& SESS_INITIALLOGIN1
) {
391 sess
->flags
&= ~SESS_INITIALLOGIN1
;
393 addText(sp
, "SessionType=%s", op
->sessionType
);
394 addText(sp
, "InitiatorName=%s", op
->initiatorName
);
395 if(strcmp(op
->sessionType
, "Discovery") != 0) {
396 addText(sp
, "TargetName=%s", op
->targetName
);
400 case SN_PHASE
: // Security Negotiation
401 addText(sp
, "AuthMethod=%s", op
->authMethod
);
404 case LON_PHASE
: // Login Operational Negotiation
405 if((sess
->flags
& SESS_NEGODONE
) == 0) {
406 sess
->flags
|= SESS_NEGODONE
;
407 addText(sp
, "MaxBurstLength=%d", op
->maxBurstLength
);
408 addText(sp
, "HeaderDigest=%s", op
->headerDigest
);
409 addText(sp
, "DataDigest=%s", op
->dataDigest
);
410 addText(sp
, "MaxRecvDataSegmentLength=%d", op
->maxRecvDataSegmentLength
);
411 addText(sp
, "ErrorRecoveryLevel=%d", op
->errorRecoveryLevel
);
412 addText(sp
, "DefaultTime2Wait=%d", op
->defaultTime2Wait
);
413 addText(sp
, "DefaultTime2Retain=%d", op
->defaultTime2Retain
);
414 addText(sp
, "DataPDUInOrder=%s", op
->dataPDUInOrder
? "Yes": "No");
415 addText(sp
, "DataSequenceInOrder=%s", op
->dataSequenceInOrder
? "Yes": "No");
416 addText(sp
, "MaxOutstandingR2T=%d", op
->maxOutstandingR2T
);
418 if(strcmp(op
->sessionType
, "Discovery") != 0) {
419 addText(sp
, "MaxConnections=%d", op
->maxConnections
);
420 addText(sp
, "FirstBurstLength=%d", op
->firstBurstLength
);
421 addText(sp
, "InitialR2T=%s", op
->initialR2T
? "Yes": "No");
422 addText(sp
, "ImmediateData=%s", op
->immediateData
? "Yes": "No");
429 status
= sendPDU(sess
, &spp
, handleLoginResp
);
432 case 0: // all is ok ...
433 if(sess
->csg
== SN_PHASE
)
435 | if we are still here, then we need
436 | to exchange some secrets ...
438 status
= authenticate(sess
);