login.c revision 211095
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: head/sbin/iscontrol/login.c 211095 2010-08-09 12:36:36Z des $");
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#if __FreeBSD_version < 500000
43171568Sscottl#include <sys/time.h>
44171568Sscottl#endif
45171568Sscottl#include <sys/ioctl.h>
46171568Sscottl#include <stdio.h>
47171568Sscottl#include <stdlib.h>
48171568Sscottl#include <string.h>
49171568Sscottl
50211095Sdes#include <dev/iscsi/initiator/iscsi.h>
51171568Sscottl#include "iscontrol.h"
52171568Sscottl
53171568Sscottlstatic char *status_class1[] = {
54171568Sscottl     "Initiator error",
55171568Sscottl     "Authentication failure",
56171568Sscottl     "Authorization failure",
57171568Sscottl     "Not found",
58171568Sscottl     "Target removed",
59171568Sscottl     "Unsupported version",
60171568Sscottl     "Too many connections",
61171568Sscottl     "Missing parameter",
62171568Sscottl     "Can't include in session",
63171568Sscottl     "Session type not suported",
64171568Sscottl     "Session does not exist",
65171568Sscottl     "Invalid during login",
66171568Sscottl};
67171568Sscottl#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
68171568Sscottl
69171568Sscottlstatic char *status_class3[] = {
70171568Sscottl     "Target error",
71171568Sscottl     "Service unavailable",
72171568Sscottl     "Out of resources"
73171568Sscottl};
74171568Sscottl#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
75171568Sscottl
76171568Sscottlstatic char *
77171568SscottlselectFrom(char *str, token_t *list)
78171568Sscottl{
79171568Sscottl     char	*sep, *sp;
80171568Sscottl     token_t	*lp;
81171568Sscottl     int	n;
82171568Sscottl
83171568Sscottl     sp = str;
84171568Sscottl     do {
85171568Sscottl	  sep = strchr(sp, ',');
86171568Sscottl	  if(sep != NULL)
87171568Sscottl	       n = sep - sp;
88171568Sscottl	  else
89171568Sscottl	       n = strlen(sp);
90171568Sscottl
91171568Sscottl	  for(lp = list; lp->name != NULL; lp++) {
92171568Sscottl	       if(strncasecmp(lp->name, sp, n) == 0)
93171568Sscottl		    return strdup(lp->name);
94171568Sscottl	  }
95171568Sscottl	  sp = sep + 1;
96171568Sscottl     } while(sep != NULL);
97171568Sscottl
98171568Sscottl     return NULL;
99171568Sscottl}
100171568Sscottl
101171568Sscottlstatic char *
102171568Sscottlgetkeyval(char *key, pdu_t *pp)
103171568Sscottl{
104171568Sscottl    char	*ptr;
105171568Sscottl    int	klen, len, n;
106171568Sscottl
107171568Sscottl    debug_called(3);
108171568Sscottl
109171568Sscottl    len = pp->ds_len;
110211095Sdes    ptr = (char *)pp->ds_addr;
111171568Sscottl    klen = strlen(key);
112171568Sscottl    while(len > klen) {
113171568Sscottl	 if(strncmp(key, ptr, klen) == 0)
114171568Sscottl	      return ptr+klen;
115171568Sscottl	 n = strlen(ptr) + 1;
116171568Sscottl	 len -= n;
117171568Sscottl	 ptr += n;
118171568Sscottl    }
119171568Sscottl    return 0;
120171568Sscottl}
121171568Sscottl
122171568Sscottlstatic int
123171568SscottlhandleTgtResp(isess_t *sess, pdu_t *pp)
124171568Sscottl{
125171568Sscottl     isc_opt_t	*op = sess->op;
126171568Sscottl     char	*np, *rp, *d1, *d2;
127171568Sscottl     int	res, l1, l2;
128171568Sscottl
129171568Sscottl     res = -1;
130171568Sscottl     if(((np = getkeyval("CHAP_N=", pp)) == NULL) ||
131171568Sscottl	((rp = getkeyval("CHAP_R=", pp)) == NULL))
132171568Sscottl	  goto out;
133171568Sscottl     if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) {
134171568Sscottl	  fprintf(stderr, "%s does not match\n", np);
135171568Sscottl	  goto out;
136171568Sscottl     }
137171568Sscottl     l1 = str2bin(op->tgtChapDigest, &d1);
138171568Sscottl     l2 = str2bin(rp, &d2);
139171568Sscottl
140171568Sscottl     debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp);
141171568Sscottl     if(l1 == l2 && memcmp(d1, d2, l1) == 0)
142171568Sscottl	res = 0;
143171568Sscottl     if(l1)
144171568Sscottl	  free(d1);
145171568Sscottl     if(l2)
146171568Sscottl	  free(d2);
147171568Sscottl out:
148171568Sscottl     free(op->tgtChapDigest);
149171568Sscottl     op->tgtChapDigest = NULL;
150171568Sscottl
151171568Sscottl     debug(3, "res=%d", res);
152171568Sscottl
153171568Sscottl     return res;
154171568Sscottl}
155171568Sscottl
156171568Sscottlstatic void
157171568SscottlprocessParams(isess_t *sess, pdu_t *pp)
158171568Sscottl{
159171568Sscottl     isc_opt_t		*op = sess->op;
160171568Sscottl     int		len, klen, n;
161171568Sscottl     char		*eq, *ptr;
162171568Sscottl
163171568Sscottl     debug_called(3);
164171568Sscottl
165171568Sscottl     len = pp->ds_len;
166211095Sdes     ptr = (char *)pp->ds_addr;
167171568Sscottl     while(len > 0) {
168171568Sscottl	  if(vflag > 1)
169171568Sscottl	       printf("got: len=%d %s\n", len, ptr);
170171568Sscottl	  klen = 0;
171171568Sscottl	  if((eq = strchr(ptr, '=')) != NULL)
172171568Sscottl	       klen = eq - ptr;
173171568Sscottl	  if(klen > 0) {
174171568Sscottl	       if(strncmp(ptr, "TargetAddress", klen) == 0) {
175185289Sscottl		    char	*p, *q, *ta = NULL;
176171568Sscottl
177171568Sscottl		    // TargetAddress=domainname[:port][,portal-group-tag]
178171568Sscottl		    // XXX: if(op->targetAddress) free(op->targetAddress);
179171568Sscottl		    q = op->targetAddress = strdup(eq+1);
180171568Sscottl		    if(*q == '[') {
181171568Sscottl			 // bracketed IPv6
182185289Sscottl			 if((q = strchr(q, ']')) != NULL) {
183185289Sscottl			      *q++ = '\0';
184185289Sscottl			      ta = op->targetAddress;
185185289Sscottl			      op->targetAddress = strdup(ta+1);
186185289Sscottl			 } else
187171568Sscottl			      q = op->targetAddress;
188171568Sscottl		    }
189171568Sscottl		    if((p = strchr(q, ',')) != NULL) {
190171568Sscottl			 *p++ = 0;
191171568Sscottl			 op->targetPortalGroupTag = atoi(p);
192171568Sscottl		    }
193171568Sscottl		    if((p = strchr(q, ':')) != NULL) {
194171568Sscottl			 *p++ = 0;
195171568Sscottl			 op->port = atoi(p);
196171568Sscottl		    }
197185289Sscottl		    if(ta)
198185289Sscottl			 free(ta);
199171568Sscottl	       } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) {
200171568Sscottl		    // danny's RFC
201171568Sscottl		    op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0);
202171568Sscottl	       } else  if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) {
203171568Sscottl		    op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0);
204171568Sscottl	       } else if(strncmp(ptr, "HeaderDigest", klen) == 0) {
205171568Sscottl		    op->headerDigest = selectFrom(eq+1, DigestMethods);
206171568Sscottl	       } else if(strncmp(ptr, "DataDigest", klen) == 0) {
207171568Sscottl		    op->dataDigest = selectFrom(eq+1, DigestMethods);
208171568Sscottl	       } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0)
209171568Sscottl		    op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0);
210171568Sscottl#if 0
211171568Sscottl	       else
212171568Sscottl	       for(kp = keyMap; kp->name; kp++) {
213171568Sscottl		    if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=')
214171568Sscottl			 mp->func(sess, ptr+kp->len+1, GET);
215171568Sscottl	       }
216171568Sscottl#endif
217171568Sscottl	  }
218171568Sscottl	  n = strlen(ptr) + 1;
219171568Sscottl	  len -= n;
220171568Sscottl	  ptr += n;
221171568Sscottl     }
222171568Sscottl
223171568Sscottl}
224171568Sscottl
225171568Sscottlstatic int
226171568SscottlhandleLoginResp(isess_t *sess, pdu_t *pp)
227171568Sscottl{
228171568Sscottl     login_rsp_t *lp = (login_rsp_t *)pp;
229171568Sscottl     uint	st_class, status = ntohs(lp->status);
230171568Sscottl
231171568Sscottl     debug_called(3);
232171568Sscottl     debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status);
233171568Sscottl
234171568Sscottl     st_class  = status >> 8;
235171568Sscottl     if(status) {
236211095Sdes	  uint	st_detail = status & 0xff;
237171568Sscottl
238171568Sscottl	  switch(st_class) {
239171568Sscottl	  case 1: // Redirect
240171568Sscottl	       switch(st_detail) {
241171568Sscottl		    // the ITN (iSCSI target Name) requests a:
242171568Sscottl	       case 1: // temporary address change
243171568Sscottl	       case 2: // permanent address change
244171568Sscottl		    status = 0;
245171568Sscottl	       }
246171568Sscottl	       break;
247171568Sscottl
248171568Sscottl	  case 2: // Initiator Error
249171568Sscottl	       if(st_detail < CLASS1_ERRS)
250171568Sscottl		    printf("0x%04x: %s\n", status, status_class1[st_detail]);
251171568Sscottl	       break;
252171568Sscottl
253171568Sscottl	  case 3:
254171568Sscottl	       if(st_detail < CLASS3_ERRS)
255171568Sscottl		    printf("0x%04x: %s\n", status, status_class3[st_detail]);
256171568Sscottl	       break;
257171568Sscottl	  }
258171568Sscottl     }
259171568Sscottl
260171568Sscottl     if(status == 0) {
261171568Sscottl	  processParams(sess, pp);
262171568Sscottl	  setOptions(sess, 0); // XXX: just in case ...
263171568Sscottl
264171568Sscottl	  if(lp->T) {
265171568Sscottl	       isc_opt_t	*op = sess->op;
266171568Sscottl
267171568Sscottl	       if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL))
268171568Sscottl		    if(handleTgtResp(sess, pp) != 0)
269171568Sscottl			 return 1; // XXX: Authentication failure ...
270171568Sscottl	       sess->csg = lp->NSG;
271171568Sscottl	       if(sess->csg == FF_PHASE) {
272171568Sscottl		    // XXX: will need this when implementing reconnect.
273171568Sscottl		    sess->tsih = lp->tsih;
274171568Sscottl		    debug(2, "TSIH=%x", sess->tsih);
275171568Sscottl	       }
276171568Sscottl	  }
277171568Sscottl     }
278171568Sscottl
279171568Sscottl     return st_class;
280171568Sscottl}
281171568Sscottl
282171568Sscottlstatic int
283171568SscottlhandleChap(isess_t *sess, pdu_t *pp)
284171568Sscottl{
285171568Sscottl     pdu_t		spp;
286171568Sscottl     login_req_t	*lp;
287171568Sscottl     isc_opt_t		*op = sess->op;
288171568Sscottl     char		*ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits
289171568Sscottl
290171568Sscottl     debug_called(3);
291171568Sscottl
292171568Sscottl     bzero(&spp, sizeof(pdu_t));
293171568Sscottl     lp = (login_req_t *)&spp.ipdu.bhs;
294171568Sscottl     lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
295171568Sscottl     memcpy(lp->isid, sess->isid, 6);
296171568Sscottl     lp->tsih = sess->tsih;    // MUST be zero the first time!
297171568Sscottl     lp->CID = htons(1);
298171568Sscottl     lp->CSG = SN_PHASE;       // Security Negotiation
299171568Sscottl     lp->NSG = LON_PHASE;
300171568Sscottl     lp->T = 1;
301171568Sscottl
302171568Sscottl     if(((ap = getkeyval("CHAP_A=", pp)) == NULL) ||
303171568Sscottl	((ip = getkeyval("CHAP_I=", pp)) == NULL) ||
304171568Sscottl	((cp = getkeyval("CHAP_C=", pp)) == NULL))
305171568Sscottl	  return -1;
306171568Sscottl
307171568Sscottl     if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL)
308171568Sscottl	  return -1;
309171568Sscottl
310171568Sscottl     addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName);
311171568Sscottl     addText(&spp, "CHAP_R=%s", digest);
312171568Sscottl     free(digest);
313171568Sscottl
314171568Sscottl     if(op->tgtChapSecret != NULL) {
315171568Sscottl	  op->tgtChapID = (random() >> 24) % 255; // should be random enough ...
316171568Sscottl	  addText(&spp, "CHAP_I=%d", op->tgtChapID);
317171568Sscottl	  cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8);
318171568Sscottl	  addText(&spp, "CHAP_C=%s", cp);
319171568Sscottl	  op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret);
320171568Sscottl     }
321171568Sscottl
322171568Sscottl     return sendPDU(sess, &spp, handleLoginResp);
323171568Sscottl}
324171568Sscottl
325171568Sscottlstatic int
326171568Sscottlauthenticate(isess_t *sess)
327171568Sscottl{
328171568Sscottl     pdu_t		spp;
329171568Sscottl     login_req_t	*lp;
330171568Sscottl     isc_opt_t	*op = sess->op;
331171568Sscottl
332171568Sscottl     bzero(&spp, sizeof(pdu_t));
333171568Sscottl     lp = (login_req_t *)&spp.ipdu.bhs;
334171568Sscottl     lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
335171568Sscottl     memcpy(lp->isid, sess->isid, 6);
336171568Sscottl     lp->tsih = sess->tsih;	// MUST be zero the first time!
337171568Sscottl     lp->CID = htons(1);
338171568Sscottl     lp->CSG = SN_PHASE;	// Security Negotiation
339171568Sscottl     lp->NSG = SN_PHASE;
340171568Sscottl     lp->T = 0;
341171568Sscottl
342171568Sscottl     switch((authm_t)lookup(AuthMethods, op->authMethod)) {
343171568Sscottl     case NONE:
344171568Sscottl	  return 0;
345171568Sscottl
346171568Sscottl     case KRB5:
347171568Sscottl     case SPKM1:
348171568Sscottl     case SPKM2:
349171568Sscottl     case SRP:
350171568Sscottl	  return 2;
351171568Sscottl
352171568Sscottl     case CHAP:
353171568Sscottl	  if(op->chapDigest == 0)
354171568Sscottl	       addText(&spp, "CHAP_A=5");
355171568Sscottl	  else
356171568Sscottl	  if(strcmp(op->chapDigest, "MD5") == 0)
357171568Sscottl	       addText(&spp, "CHAP_A=5");
358171568Sscottl	  else
359171568Sscottl	  if(strcmp(op->chapDigest, "SHA1") == 0)
360171568Sscottl	       addText(&spp, "CHAP_A=7");
361171568Sscottl	  else
362171568Sscottl	       addText(&spp, "CHAP_A=5,7");
363171568Sscottl	  return sendPDU(sess, &spp, handleChap);
364171568Sscottl     }
365171568Sscottl     return 1;
366171568Sscottl}
367171568Sscottl
368171568Sscottlint
369171568SscottlloginPhase(isess_t *sess)
370171568Sscottl{
371171568Sscottl     pdu_t		spp, *sp = &spp;
372171568Sscottl     isc_opt_t  	*op = sess->op;
373171568Sscottl     login_req_t	*lp;
374171568Sscottl     int		status = 1;
375171568Sscottl
376171568Sscottl     debug_called(3);
377171568Sscottl
378171568Sscottl     bzero(sp, sizeof(pdu_t));
379171568Sscottl     lp = (login_req_t *)&spp.ipdu.bhs;
380171568Sscottl     lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
381171568Sscottl     memcpy(lp->isid, sess->isid, 6);
382171568Sscottl     lp->tsih = sess->tsih;	// MUST be zero the first time!
383171568Sscottl     lp->CID = htons(1);	// sess->cid?
384171568Sscottl
385171568Sscottl     if((lp->CSG = sess->csg) == LON_PHASE)
386171568Sscottl	  lp->NSG = FF_PHASE;	// lets try and go full feature ...
387171568Sscottl     else
388171568Sscottl	  lp->NSG = LON_PHASE;
389171568Sscottl     lp->T = 1;			// transit to next login stage
390171568Sscottl
391171568Sscottl     if(sess->flags & SESS_INITIALLOGIN1) {
392171568Sscottl	  sess->flags &= ~SESS_INITIALLOGIN1;
393171568Sscottl
394171568Sscottl	  addText(sp, "SessionType=%s", op->sessionType);
395171568Sscottl	  addText(sp, "InitiatorName=%s", op->initiatorName);
396171568Sscottl	  if(strcmp(op->sessionType, "Discovery") != 0) {
397171568Sscottl	       addText(sp, "TargetName=%s", op->targetName);
398171568Sscottl	  }
399171568Sscottl     }
400171568Sscottl     switch(sess->csg) {
401171568Sscottl     case SN_PHASE:	// Security Negotiation
402171568Sscottl	  addText(sp, "AuthMethod=%s", op->authMethod);
403171568Sscottl	  break;
404171568Sscottl
405171568Sscottl     case LON_PHASE:	// Login Operational Negotiation
406171568Sscottl	  if((sess->flags & SESS_NEGODONE) == 0) {
407171568Sscottl	       sess->flags |= SESS_NEGODONE;
408171568Sscottl	       addText(sp, "MaxBurstLength=%d", op->maxBurstLength);
409171568Sscottl	       addText(sp, "HeaderDigest=%s", op->headerDigest);
410171568Sscottl	       addText(sp, "DataDigest=%s", op->dataDigest);
411171568Sscottl	       addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength);
412171568Sscottl	       addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel);
413171568Sscottl	       addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait);
414171568Sscottl	       addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain);
415171568Sscottl	       addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No");
416171568Sscottl	       addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No");
417171568Sscottl	       addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T);
418171568Sscottl
419171568Sscottl	       if(strcmp(op->sessionType, "Discovery") != 0) {
420171568Sscottl		    addText(sp, "MaxConnections=%d", op->maxConnections);
421171568Sscottl		    addText(sp, "FirstBurstLength=%d", op->firstBurstLength);
422171568Sscottl		    addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No");
423171568Sscottl		    addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No");
424171568Sscottl	       }
425171568Sscottl	  }
426171568Sscottl
427171568Sscottl	  break;
428171568Sscottl     }
429171568Sscottl
430171568Sscottl     status = sendPDU(sess, &spp, handleLoginResp);
431171568Sscottl
432171568Sscottl     switch(status) {
433171568Sscottl     case 0: // all is ok ...
434171568Sscottl	  if(sess->csg == SN_PHASE)
435171568Sscottl	       /*
436171568Sscottl		| if we are still here, then we need
437171568Sscottl		| to exchange some secrets ...
438171568Sscottl	        */
439171568Sscottl	       status = authenticate(sess);
440171568Sscottl     }
441171568Sscottl
442171568Sscottl     return status;
443171568Sscottl}
444