1171568Sscottl/*-
2211095Sdes * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il>
3171568Sscottl * All rights reserved.
4171568Sscottl *
5171568Sscottl * Redistribution and use in source and binary forms, with or without
6171568Sscottl * modification, are permitted provided that the following conditions
7171568Sscottl * are met:
8171568Sscottl * 1. Redistributions of source code must retain the above copyright
9171568Sscottl *    notice, this list of conditions and the following disclaimer.
10171568Sscottl * 2. Redistributions in binary form must reproduce the above copyright
11171568Sscottl *    notice, this list of conditions and the following disclaimer in the
12171568Sscottl *    documentation and/or other materials provided with the distribution.
13171568Sscottl *
14171568Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15171568Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16171568Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17171568Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18171568Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19171568Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20171568Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21171568Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22171568Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23171568Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24171568Sscottl * SUCH DAMAGE.
25171568Sscottl *
26171568Sscottl */
27171568Sscottl/*
28171568Sscottl | $Id: login.c,v 1.4 2007/04/27 07:40:40 danny Exp danny $
29171568Sscottl */
30171568Sscottl
31171568Sscottl#include <sys/cdefs.h>
32171568Sscottl__FBSDID("$FreeBSD$");
33171568Sscottl
34171568Sscottl#include <sys/param.h>
35171568Sscottl#include <sys/types.h>
36171568Sscottl#include <sys/socket.h>
37171568Sscottl#include <sys/sysctl.h>
38171568Sscottl
39171568Sscottl#include <netinet/in.h>
40171568Sscottl#include <netinet/tcp.h>
41171568Sscottl#include <arpa/inet.h>
42171568Sscottl#include <sys/ioctl.h>
43171568Sscottl#include <stdio.h>
44171568Sscottl#include <stdlib.h>
45171568Sscottl#include <string.h>
46171568Sscottl
47211095Sdes#include <dev/iscsi/initiator/iscsi.h>
48171568Sscottl#include "iscontrol.h"
49171568Sscottl
50171568Sscottlstatic char *status_class1[] = {
51171568Sscottl     "Initiator error",
52171568Sscottl     "Authentication failure",
53171568Sscottl     "Authorization failure",
54171568Sscottl     "Not found",
55171568Sscottl     "Target removed",
56171568Sscottl     "Unsupported version",
57171568Sscottl     "Too many connections",
58171568Sscottl     "Missing parameter",
59171568Sscottl     "Can't include in session",
60171568Sscottl     "Session type not suported",
61171568Sscottl     "Session does not exist",
62171568Sscottl     "Invalid during login",
63171568Sscottl};
64171568Sscottl#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
65171568Sscottl
66171568Sscottlstatic char *status_class3[] = {
67171568Sscottl     "Target error",
68171568Sscottl     "Service unavailable",
69171568Sscottl     "Out of resources"
70171568Sscottl};
71171568Sscottl#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
72171568Sscottl
73171568Sscottlstatic char *
74171568SscottlselectFrom(char *str, token_t *list)
75171568Sscottl{
76171568Sscottl     char	*sep, *sp;
77171568Sscottl     token_t	*lp;
78171568Sscottl     int	n;
79171568Sscottl
80171568Sscottl     sp = str;
81171568Sscottl     do {
82171568Sscottl	  sep = strchr(sp, ',');
83171568Sscottl	  if(sep != NULL)
84171568Sscottl	       n = sep - sp;
85171568Sscottl	  else
86171568Sscottl	       n = strlen(sp);
87171568Sscottl
88171568Sscottl	  for(lp = list; lp->name != NULL; lp++) {
89171568Sscottl	       if(strncasecmp(lp->name, sp, n) == 0)
90171568Sscottl		    return strdup(lp->name);
91171568Sscottl	  }
92171568Sscottl	  sp = sep + 1;
93171568Sscottl     } while(sep != NULL);
94171568Sscottl
95171568Sscottl     return NULL;
96171568Sscottl}
97171568Sscottl
98171568Sscottlstatic char *
99171568Sscottlgetkeyval(char *key, pdu_t *pp)
100171568Sscottl{
101171568Sscottl    char	*ptr;
102171568Sscottl    int	klen, len, n;
103171568Sscottl
104171568Sscottl    debug_called(3);
105171568Sscottl
106171568Sscottl    len = pp->ds_len;
107211095Sdes    ptr = (char *)pp->ds_addr;
108171568Sscottl    klen = strlen(key);
109171568Sscottl    while(len > klen) {
110171568Sscottl	 if(strncmp(key, ptr, klen) == 0)
111171568Sscottl	      return ptr+klen;
112171568Sscottl	 n = strlen(ptr) + 1;
113171568Sscottl	 len -= n;
114171568Sscottl	 ptr += n;
115171568Sscottl    }
116171568Sscottl    return 0;
117171568Sscottl}
118171568Sscottl
119171568Sscottlstatic int
120171568SscottlhandleTgtResp(isess_t *sess, pdu_t *pp)
121171568Sscottl{
122171568Sscottl     isc_opt_t	*op = sess->op;
123171568Sscottl     char	*np, *rp, *d1, *d2;
124171568Sscottl     int	res, l1, l2;
125171568Sscottl
126171568Sscottl     res = -1;
127171568Sscottl     if(((np = getkeyval("CHAP_N=", pp)) == NULL) ||
128171568Sscottl	((rp = getkeyval("CHAP_R=", pp)) == NULL))
129171568Sscottl	  goto out;
130171568Sscottl     if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) {
131171568Sscottl	  fprintf(stderr, "%s does not match\n", np);
132171568Sscottl	  goto out;
133171568Sscottl     }
134171568Sscottl     l1 = str2bin(op->tgtChapDigest, &d1);
135171568Sscottl     l2 = str2bin(rp, &d2);
136171568Sscottl
137171568Sscottl     debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp);
138171568Sscottl     if(l1 == l2 && memcmp(d1, d2, l1) == 0)
139171568Sscottl	res = 0;
140171568Sscottl     if(l1)
141171568Sscottl	  free(d1);
142171568Sscottl     if(l2)
143171568Sscottl	  free(d2);
144171568Sscottl out:
145171568Sscottl     free(op->tgtChapDigest);
146171568Sscottl     op->tgtChapDigest = NULL;
147171568Sscottl
148171568Sscottl     debug(3, "res=%d", res);
149171568Sscottl
150171568Sscottl     return res;
151171568Sscottl}
152171568Sscottl
153171568Sscottlstatic void
154171568SscottlprocessParams(isess_t *sess, pdu_t *pp)
155171568Sscottl{
156171568Sscottl     isc_opt_t		*op = sess->op;
157171568Sscottl     int		len, klen, n;
158171568Sscottl     char		*eq, *ptr;
159171568Sscottl
160171568Sscottl     debug_called(3);
161171568Sscottl
162171568Sscottl     len = pp->ds_len;
163211095Sdes     ptr = (char *)pp->ds_addr;
164171568Sscottl     while(len > 0) {
165171568Sscottl	  if(vflag > 1)
166171568Sscottl	       printf("got: len=%d %s\n", len, ptr);
167171568Sscottl	  klen = 0;
168171568Sscottl	  if((eq = strchr(ptr, '=')) != NULL)
169171568Sscottl	       klen = eq - ptr;
170171568Sscottl	  if(klen > 0) {
171171568Sscottl	       if(strncmp(ptr, "TargetAddress", klen) == 0) {
172185289Sscottl		    char	*p, *q, *ta = NULL;
173171568Sscottl
174171568Sscottl		    // TargetAddress=domainname[:port][,portal-group-tag]
175171568Sscottl		    // XXX: if(op->targetAddress) free(op->targetAddress);
176171568Sscottl		    q = op->targetAddress = strdup(eq+1);
177171568Sscottl		    if(*q == '[') {
178171568Sscottl			 // bracketed IPv6
179185289Sscottl			 if((q = strchr(q, ']')) != NULL) {
180185289Sscottl			      *q++ = '\0';
181185289Sscottl			      ta = op->targetAddress;
182185289Sscottl			      op->targetAddress = strdup(ta+1);
183185289Sscottl			 } else
184171568Sscottl			      q = op->targetAddress;
185171568Sscottl		    }
186171568Sscottl		    if((p = strchr(q, ',')) != NULL) {
187171568Sscottl			 *p++ = 0;
188171568Sscottl			 op->targetPortalGroupTag = atoi(p);
189171568Sscottl		    }
190171568Sscottl		    if((p = strchr(q, ':')) != NULL) {
191171568Sscottl			 *p++ = 0;
192171568Sscottl			 op->port = atoi(p);
193171568Sscottl		    }
194185289Sscottl		    if(ta)
195185289Sscottl			 free(ta);
196171568Sscottl	       } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) {
197171568Sscottl		    // danny's RFC
198171568Sscottl		    op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0);
199171568Sscottl	       } else  if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) {
200171568Sscottl		    op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0);
201171568Sscottl	       } else if(strncmp(ptr, "HeaderDigest", klen) == 0) {
202171568Sscottl		    op->headerDigest = selectFrom(eq+1, DigestMethods);
203171568Sscottl	       } else if(strncmp(ptr, "DataDigest", klen) == 0) {
204171568Sscottl		    op->dataDigest = selectFrom(eq+1, DigestMethods);
205171568Sscottl	       } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0)
206171568Sscottl		    op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0);
207171568Sscottl#if 0
208171568Sscottl	       else
209171568Sscottl	       for(kp = keyMap; kp->name; kp++) {
210171568Sscottl		    if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=')
211171568Sscottl			 mp->func(sess, ptr+kp->len+1, GET);
212171568Sscottl	       }
213171568Sscottl#endif
214171568Sscottl	  }
215171568Sscottl	  n = strlen(ptr) + 1;
216171568Sscottl	  len -= n;
217171568Sscottl	  ptr += n;
218171568Sscottl     }
219171568Sscottl
220171568Sscottl}
221171568Sscottl
222171568Sscottlstatic int
223171568SscottlhandleLoginResp(isess_t *sess, pdu_t *pp)
224171568Sscottl{
225171568Sscottl     login_rsp_t *lp = (login_rsp_t *)pp;
226171568Sscottl     uint	st_class, status = ntohs(lp->status);
227171568Sscottl
228171568Sscottl     debug_called(3);
229171568Sscottl     debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status);
230171568Sscottl
231171568Sscottl     st_class  = status >> 8;
232171568Sscottl     if(status) {
233211095Sdes	  uint	st_detail = status & 0xff;
234171568Sscottl
235171568Sscottl	  switch(st_class) {
236171568Sscottl	  case 1: // Redirect
237171568Sscottl	       switch(st_detail) {
238171568Sscottl		    // the ITN (iSCSI target Name) requests a:
239171568Sscottl	       case 1: // temporary address change
240171568Sscottl	       case 2: // permanent address change
241171568Sscottl		    status = 0;
242171568Sscottl	       }
243171568Sscottl	       break;
244171568Sscottl
245171568Sscottl	  case 2: // Initiator Error
246171568Sscottl	       if(st_detail < CLASS1_ERRS)
247171568Sscottl		    printf("0x%04x: %s\n", status, status_class1[st_detail]);
248171568Sscottl	       break;
249171568Sscottl
250171568Sscottl	  case 3:
251171568Sscottl	       if(st_detail < CLASS3_ERRS)
252171568Sscottl		    printf("0x%04x: %s\n", status, status_class3[st_detail]);
253171568Sscottl	       break;
254171568Sscottl	  }
255171568Sscottl     }
256171568Sscottl
257171568Sscottl     if(status == 0) {
258171568Sscottl	  processParams(sess, pp);
259171568Sscottl	  setOptions(sess, 0); // XXX: just in case ...
260171568Sscottl
261171568Sscottl	  if(lp->T) {
262171568Sscottl	       isc_opt_t	*op = sess->op;
263171568Sscottl
264171568Sscottl	       if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL))
265171568Sscottl		    if(handleTgtResp(sess, pp) != 0)
266171568Sscottl			 return 1; // XXX: Authentication failure ...
267171568Sscottl	       sess->csg = lp->NSG;
268171568Sscottl	       if(sess->csg == FF_PHASE) {
269171568Sscottl		    // XXX: will need this when implementing reconnect.
270171568Sscottl		    sess->tsih = lp->tsih;
271171568Sscottl		    debug(2, "TSIH=%x", sess->tsih);
272171568Sscottl	       }
273171568Sscottl	  }
274171568Sscottl     }
275171568Sscottl
276171568Sscottl     return st_class;
277171568Sscottl}
278171568Sscottl
279171568Sscottlstatic int
280171568SscottlhandleChap(isess_t *sess, pdu_t *pp)
281171568Sscottl{
282171568Sscottl     pdu_t		spp;
283171568Sscottl     login_req_t	*lp;
284171568Sscottl     isc_opt_t		*op = sess->op;
285171568Sscottl     char		*ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits
286171568Sscottl
287171568Sscottl     debug_called(3);
288171568Sscottl
289171568Sscottl     bzero(&spp, sizeof(pdu_t));
290171568Sscottl     lp = (login_req_t *)&spp.ipdu.bhs;
291171568Sscottl     lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
292171568Sscottl     memcpy(lp->isid, sess->isid, 6);
293171568Sscottl     lp->tsih = sess->tsih;    // MUST be zero the first time!
294171568Sscottl     lp->CID = htons(1);
295171568Sscottl     lp->CSG = SN_PHASE;       // Security Negotiation
296171568Sscottl     lp->NSG = LON_PHASE;
297171568Sscottl     lp->T = 1;
298171568Sscottl
299171568Sscottl     if(((ap = getkeyval("CHAP_A=", pp)) == NULL) ||
300171568Sscottl	((ip = getkeyval("CHAP_I=", pp)) == NULL) ||
301171568Sscottl	((cp = getkeyval("CHAP_C=", pp)) == NULL))
302171568Sscottl	  return -1;
303171568Sscottl
304171568Sscottl     if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL)
305171568Sscottl	  return -1;
306171568Sscottl
307171568Sscottl     addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName);
308171568Sscottl     addText(&spp, "CHAP_R=%s", digest);
309171568Sscottl     free(digest);
310171568Sscottl
311171568Sscottl     if(op->tgtChapSecret != NULL) {
312171568Sscottl	  op->tgtChapID = (random() >> 24) % 255; // should be random enough ...
313171568Sscottl	  addText(&spp, "CHAP_I=%d", op->tgtChapID);
314171568Sscottl	  cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8);
315171568Sscottl	  addText(&spp, "CHAP_C=%s", cp);
316171568Sscottl	  op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret);
317171568Sscottl     }
318171568Sscottl
319171568Sscottl     return sendPDU(sess, &spp, handleLoginResp);
320171568Sscottl}
321171568Sscottl
322171568Sscottlstatic int
323171568Sscottlauthenticate(isess_t *sess)
324171568Sscottl{
325171568Sscottl     pdu_t		spp;
326171568Sscottl     login_req_t	*lp;
327171568Sscottl     isc_opt_t	*op = sess->op;
328171568Sscottl
329171568Sscottl     bzero(&spp, sizeof(pdu_t));
330171568Sscottl     lp = (login_req_t *)&spp.ipdu.bhs;
331171568Sscottl     lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
332171568Sscottl     memcpy(lp->isid, sess->isid, 6);
333171568Sscottl     lp->tsih = sess->tsih;	// MUST be zero the first time!
334171568Sscottl     lp->CID = htons(1);
335171568Sscottl     lp->CSG = SN_PHASE;	// Security Negotiation
336171568Sscottl     lp->NSG = SN_PHASE;
337171568Sscottl     lp->T = 0;
338171568Sscottl
339171568Sscottl     switch((authm_t)lookup(AuthMethods, op->authMethod)) {
340171568Sscottl     case NONE:
341171568Sscottl	  return 0;
342171568Sscottl
343171568Sscottl     case KRB5:
344171568Sscottl     case SPKM1:
345171568Sscottl     case SPKM2:
346171568Sscottl     case SRP:
347171568Sscottl	  return 2;
348171568Sscottl
349171568Sscottl     case CHAP:
350171568Sscottl	  if(op->chapDigest == 0)
351171568Sscottl	       addText(&spp, "CHAP_A=5");
352171568Sscottl	  else
353171568Sscottl	  if(strcmp(op->chapDigest, "MD5") == 0)
354171568Sscottl	       addText(&spp, "CHAP_A=5");
355171568Sscottl	  else
356171568Sscottl	  if(strcmp(op->chapDigest, "SHA1") == 0)
357171568Sscottl	       addText(&spp, "CHAP_A=7");
358171568Sscottl	  else
359171568Sscottl	       addText(&spp, "CHAP_A=5,7");
360171568Sscottl	  return sendPDU(sess, &spp, handleChap);
361171568Sscottl     }
362171568Sscottl     return 1;
363171568Sscottl}
364171568Sscottl
365171568Sscottlint
366171568SscottlloginPhase(isess_t *sess)
367171568Sscottl{
368171568Sscottl     pdu_t		spp, *sp = &spp;
369171568Sscottl     isc_opt_t  	*op = sess->op;
370171568Sscottl     login_req_t	*lp;
371171568Sscottl     int		status = 1;
372171568Sscottl
373171568Sscottl     debug_called(3);
374171568Sscottl
375171568Sscottl     bzero(sp, sizeof(pdu_t));
376171568Sscottl     lp = (login_req_t *)&spp.ipdu.bhs;
377171568Sscottl     lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
378171568Sscottl     memcpy(lp->isid, sess->isid, 6);
379171568Sscottl     lp->tsih = sess->tsih;	// MUST be zero the first time!
380171568Sscottl     lp->CID = htons(1);	// sess->cid?
381171568Sscottl
382171568Sscottl     if((lp->CSG = sess->csg) == LON_PHASE)
383171568Sscottl	  lp->NSG = FF_PHASE;	// lets try and go full feature ...
384171568Sscottl     else
385171568Sscottl	  lp->NSG = LON_PHASE;
386171568Sscottl     lp->T = 1;			// transit to next login stage
387171568Sscottl
388171568Sscottl     if(sess->flags & SESS_INITIALLOGIN1) {
389171568Sscottl	  sess->flags &= ~SESS_INITIALLOGIN1;
390171568Sscottl
391171568Sscottl	  addText(sp, "SessionType=%s", op->sessionType);
392171568Sscottl	  addText(sp, "InitiatorName=%s", op->initiatorName);
393171568Sscottl	  if(strcmp(op->sessionType, "Discovery") != 0) {
394171568Sscottl	       addText(sp, "TargetName=%s", op->targetName);
395171568Sscottl	  }
396171568Sscottl     }
397171568Sscottl     switch(sess->csg) {
398171568Sscottl     case SN_PHASE:	// Security Negotiation
399171568Sscottl	  addText(sp, "AuthMethod=%s", op->authMethod);
400171568Sscottl	  break;
401171568Sscottl
402171568Sscottl     case LON_PHASE:	// Login Operational Negotiation
403171568Sscottl	  if((sess->flags & SESS_NEGODONE) == 0) {
404171568Sscottl	       sess->flags |= SESS_NEGODONE;
405171568Sscottl	       addText(sp, "MaxBurstLength=%d", op->maxBurstLength);
406171568Sscottl	       addText(sp, "HeaderDigest=%s", op->headerDigest);
407171568Sscottl	       addText(sp, "DataDigest=%s", op->dataDigest);
408171568Sscottl	       addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength);
409171568Sscottl	       addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel);
410171568Sscottl	       addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait);
411171568Sscottl	       addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain);
412171568Sscottl	       addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No");
413171568Sscottl	       addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No");
414171568Sscottl	       addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T);
415171568Sscottl
416171568Sscottl	       if(strcmp(op->sessionType, "Discovery") != 0) {
417171568Sscottl		    addText(sp, "MaxConnections=%d", op->maxConnections);
418171568Sscottl		    addText(sp, "FirstBurstLength=%d", op->firstBurstLength);
419171568Sscottl		    addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No");
420171568Sscottl		    addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No");
421171568Sscottl	       }
422171568Sscottl	  }
423171568Sscottl
424171568Sscottl	  break;
425171568Sscottl     }
426171568Sscottl
427171568Sscottl     status = sendPDU(sess, &spp, handleLoginResp);
428171568Sscottl
429171568Sscottl     switch(status) {
430171568Sscottl     case 0: // all is ok ...
431171568Sscottl	  if(sess->csg == SN_PHASE)
432171568Sscottl	       /*
433171568Sscottl		| if we are still here, then we need
434171568Sscottl		| to exchange some secrets ...
435171568Sscottl	        */
436171568Sscottl	       status = authenticate(sess);
437171568Sscottl     }
438171568Sscottl
439171568Sscottl     return status;
440171568Sscottl}
441