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/param.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/sysctl.h>
36 #include <netinet/in.h>
37 #include <netinet/tcp.h>
38 #include <arpa/inet.h>
39 #include <sys/ioctl.h>
45 #include "iscontrol.h"
47 static char *status_class1
[] = {
49 "Authentication failure",
50 "Authorization failure",
53 "Unsupported version",
54 "Too many connections",
56 "Can't include in session",
57 "Session type not suported",
58 "Session does not exist",
59 "Invalid during login",
61 #define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
63 static char *status_class3
[] = {
65 "Service unavailable",
68 #define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
71 selectFrom(char *str
, token_t
*list
)
79 sep
= strchr(sp
, ',');
85 for(lp
= list
; lp
->name
!= NULL
; lp
++) {
86 if(strncasecmp(lp
->name
, sp
, n
) == 0)
87 return strdup(lp
->name
);
96 getkeyval(char *key
, pdu_t
*pp
)
104 ptr
= (char *)pp
->ds
;
107 if(strncmp(key
, ptr
, klen
) == 0)
117 handleTgtResp(isess_t
*sess
, pdu_t
*pp
)
119 isc_opt_t
*op
= sess
->op
;
120 char *np
, *rp
, *d1
, *d2
;
124 if(((np
= getkeyval("CHAP_N=", pp
)) == NULL
) ||
125 ((rp
= getkeyval("CHAP_R=", pp
)) == NULL
))
127 if(strcmp(np
, op
->tgtChapName
? op
->tgtChapName
: op
->initiatorName
) != 0) {
128 fprintf(stderr
, "%s does not match\n", np
);
131 l1
= str2bin(op
->tgtChapDigest
, &d1
);
132 l2
= str2bin(rp
, &d2
);
134 debug(3, "l1=%d '%s' l2=%d '%s'", l1
, op
->tgtChapDigest
, l2
, rp
);
135 if(l1
== l2
&& memcmp(d1
, d2
, l1
) == 0)
142 free(op
->tgtChapDigest
);
143 op
->tgtChapDigest
= NULL
;
145 debug(3, "res=%d", res
);
151 processParams(isess_t
*sess
, pdu_t
*pp
)
153 isc_opt_t
*op
= sess
->op
;
160 ptr
= (char *)pp
->ds
;
163 printf("got: len=%d %s\n", len
, ptr
);
165 if((eq
= strchr(ptr
, '=')) != NULL
)
168 if(strncmp(ptr
, "TargetAddress", klen
) == 0) {
169 char *p
, *q
, *ta
= NULL
;
171 // TargetAddress=domainname[:port][,portal-group-tag]
172 // XXX: if(op->targetAddress) free(op->targetAddress);
173 q
= op
->targetAddress
= strdup(eq
+1);
176 if((q
= strchr(q
, ']')) != NULL
) {
178 ta
= op
->targetAddress
;
179 op
->targetAddress
= strdup(ta
+1);
181 q
= op
->targetAddress
;
183 if((p
= strchr(q
, ',')) != NULL
) {
185 op
->targetPortalGroupTag
= atoi(p
);
187 if((p
= strchr(q
, ':')) != NULL
) {
193 } else if(strncmp(ptr
, "MaxRecvDataSegmentLength", klen
) == 0) {
195 op
->maxXmitDataSegmentLength
= strtol(eq
+1, NULL
, 0);
196 } else if(strncmp(ptr
, "TargetPortalGroupTag", klen
) == 0) {
197 op
->targetPortalGroupTag
= strtol(eq
+1, NULL
, 0);
198 } else if(strncmp(ptr
, "HeaderDigest", klen
) == 0) {
199 op
->headerDigest
= selectFrom(eq
+1, DigestMethods
);
200 } else if(strncmp(ptr
, "DataDigest", klen
) == 0) {
201 op
->dataDigest
= selectFrom(eq
+1, DigestMethods
);
202 } else if(strncmp(ptr
, "MaxOutstandingR2T", klen
) == 0)
203 op
->maxOutstandingR2T
= strtol(eq
+1, NULL
, 0);
206 for(kp
= keyMap
; kp
->name
; kp
++) {
207 if(strncmp(ptr
, kp
->name
, kp
->len
) == 0 && ptr
[kp
->len
] == '=')
208 mp
->func(sess
, ptr
+kp
->len
+1, GET
);
220 handleLoginResp(isess_t
*sess
, pdu_t
*pp
)
222 login_rsp_t
*lp
= (login_rsp_t
*)pp
;
223 uint st_class
, status
= ntohs(lp
->status
);
226 debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp
->T
, lp
->CSG
, lp
->NSG
, status
);
228 st_class
= status
>> 8;
230 unsigned int st_detail
= status
& 0xff;
235 // the ITN (iSCSI target Name) requests a:
236 case 1: // temporary address change
237 case 2: // permanent address change
242 case 2: // Initiator Error
243 if(st_detail
< CLASS1_ERRS
)
244 printf("0x%04x: %s\n", status
, status_class1
[st_detail
]);
248 if(st_detail
< CLASS3_ERRS
)
249 printf("0x%04x: %s\n", status
, status_class3
[st_detail
]);
255 processParams(sess
, pp
);
256 setOptions(sess
, 0); // XXX: just in case ...
259 isc_opt_t
*op
= sess
->op
;
261 if(sess
->csg
== SN_PHASE
&& (op
->tgtChapDigest
!= NULL
))
262 if(handleTgtResp(sess
, pp
) != 0)
263 return 1; // XXX: Authentication failure ...
265 if(sess
->csg
== FF_PHASE
) {
266 // XXX: will need this when implementing reconnect.
267 sess
->tsih
= lp
->tsih
;
268 debug(2, "TSIH=%x", sess
->tsih
);
277 handleChap(isess_t
*sess
, pdu_t
*pp
)
281 isc_opt_t
*op
= sess
->op
;
282 char *ap
, *ip
, *cp
, *digest
; // MD5 is 128bits, SHA1 160bits
286 bzero(&spp
, sizeof(pdu_t
));
287 lp
= (login_req_t
*)&spp
.ipdu
.bhs
;
288 lp
->cmd
= ISCSI_LOGIN_CMD
| 0x40; // login request + Inmediate
289 memcpy(lp
->isid
, sess
->isid
, 6);
290 lp
->tsih
= sess
->tsih
; // MUST be zero the first time!
292 lp
->CSG
= SN_PHASE
; // Security Negotiation
296 if(((ap
= getkeyval("CHAP_A=", pp
)) == NULL
) ||
297 ((ip
= getkeyval("CHAP_I=", pp
)) == NULL
) ||
298 ((cp
= getkeyval("CHAP_C=", pp
)) == NULL
))
301 if((digest
= chapDigest(ap
, (char)strtol(ip
, NULL
, 0), cp
, op
->chapSecret
)) == NULL
)
304 addText(&spp
, "CHAP_N=%s", op
->chapIName
? op
->chapIName
: op
->initiatorName
);
305 addText(&spp
, "CHAP_R=%s", digest
);
308 if(op
->tgtChapSecret
!= NULL
) {
309 op
->tgtChapID
= (random() >> 24) % 255; // should be random enough ...
310 addText(&spp
, "CHAP_I=%d", op
->tgtChapID
);
311 cp
= genChapChallenge(cp
, op
->tgtChallengeLen
? op
->tgtChallengeLen
: 8);
312 addText(&spp
, "CHAP_C=%s", cp
);
313 op
->tgtChapDigest
= chapDigest(ap
, op
->tgtChapID
, cp
, op
->tgtChapSecret
);
316 return sendPDU(sess
, &spp
, handleLoginResp
);
320 authenticate(isess_t
*sess
)
324 isc_opt_t
*op
= sess
->op
;
326 bzero(&spp
, sizeof(pdu_t
));
327 lp
= (login_req_t
*)&spp
.ipdu
.bhs
;
328 lp
->cmd
= ISCSI_LOGIN_CMD
| 0x40; // login request + Inmediate
329 memcpy(lp
->isid
, sess
->isid
, 6);
330 lp
->tsih
= sess
->tsih
; // MUST be zero the first time!
332 lp
->CSG
= SN_PHASE
; // Security Negotiation
336 switch((authm_t
)lookup(AuthMethods
, op
->authMethod
)) {
347 if(op
->chapDigest
== 0)
348 addText(&spp
, "CHAP_A=5");
350 if(strcmp(op
->chapDigest
, "MD5") == 0)
351 addText(&spp
, "CHAP_A=5");
353 if(strcmp(op
->chapDigest
, "SHA1") == 0)
354 addText(&spp
, "CHAP_A=7");
356 addText(&spp
, "CHAP_A=5,7");
357 return sendPDU(sess
, &spp
, handleChap
);
363 loginPhase(isess_t
*sess
)
365 pdu_t spp
, *sp
= &spp
;
366 isc_opt_t
*op
= sess
->op
;
372 bzero(sp
, sizeof(pdu_t
));
373 lp
= (login_req_t
*)&spp
.ipdu
.bhs
;
374 lp
->cmd
= ISCSI_LOGIN_CMD
| 0x40; // login request + Inmediate
375 memcpy(lp
->isid
, sess
->isid
, 6);
376 lp
->tsih
= sess
->tsih
; // MUST be zero the first time!
377 lp
->CID
= htons(1); // sess->cid?
379 if((lp
->CSG
= sess
->csg
) == LON_PHASE
)
380 lp
->NSG
= FF_PHASE
; // lets try and go full feature ...
383 lp
->T
= 1; // transit to next login stage
385 if(sess
->flags
& SESS_INITIALLOGIN1
) {
386 sess
->flags
&= ~SESS_INITIALLOGIN1
;
388 addText(sp
, "SessionType=%s", op
->sessionType
);
389 addText(sp
, "InitiatorName=%s", op
->initiatorName
);
390 if(strcmp(op
->sessionType
, "Discovery") != 0) {
391 addText(sp
, "TargetName=%s", op
->targetName
);
395 case SN_PHASE
: // Security Negotiation
396 addText(sp
, "AuthMethod=%s", op
->authMethod
);
399 case LON_PHASE
: // Login Operational Negotiation
400 if((sess
->flags
& SESS_NEGODONE
) == 0) {
401 sess
->flags
|= SESS_NEGODONE
;
402 addText(sp
, "MaxBurstLength=%d", op
->maxBurstLength
);
403 addText(sp
, "HeaderDigest=%s", op
->headerDigest
);
404 addText(sp
, "DataDigest=%s", op
->dataDigest
);
405 addText(sp
, "MaxRecvDataSegmentLength=%d", op
->maxRecvDataSegmentLength
);
406 addText(sp
, "ErrorRecoveryLevel=%d", op
->errorRecoveryLevel
);
407 addText(sp
, "DefaultTime2Wait=%d", op
->defaultTime2Wait
);
408 addText(sp
, "DefaultTime2Retain=%d", op
->defaultTime2Retain
);
409 addText(sp
, "DataPDUInOrder=%s", op
->dataPDUInOrder
? "Yes": "No");
410 addText(sp
, "DataSequenceInOrder=%s", op
->dataSequenceInOrder
? "Yes": "No");
411 addText(sp
, "MaxOutstandingR2T=%d", op
->maxOutstandingR2T
);
413 if(strcmp(op
->sessionType
, "Discovery") != 0) {
414 addText(sp
, "MaxConnections=%d", op
->maxConnections
);
415 addText(sp
, "FirstBurstLength=%d", op
->firstBurstLength
);
416 addText(sp
, "InitialR2T=%s", op
->initialR2T
? "Yes": "No");
417 addText(sp
, "ImmediateData=%s", op
->immediateData
? "Yes": "No");
424 status
= sendPDU(sess
, &spp
, handleLoginResp
);
427 case 0: // all is ok ...
428 if(sess
->csg
== SN_PHASE
)
430 | if we are still here, then we need
431 | to exchange some secrets ...
433 status
= authenticate(sess
);