login.c revision 171568
1171568Sscottl/*-
2171568Sscottl * Copyright (c) 2005 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 171568 2007-07-24 15:35:02Z scottl $");
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
50171568Sscottl#include "iscsi.h"
51171568Sscottl#include "iscontrol.h"
52171568Sscottl#include "pdu.h"
53171568Sscottl
54171568Sscottlstatic char *status_class1[] = {
55171568Sscottl     "Initiator error",
56171568Sscottl     "Authentication failure",
57171568Sscottl     "Authorization failure",
58171568Sscottl     "Not found",
59171568Sscottl     "Target removed",
60171568Sscottl     "Unsupported version",
61171568Sscottl     "Too many connections",
62171568Sscottl     "Missing parameter",
63171568Sscottl     "Can't include in session",
64171568Sscottl     "Session type not suported",
65171568Sscottl     "Session does not exist",
66171568Sscottl     "Invalid during login",
67171568Sscottl};
68171568Sscottl#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
69171568Sscottl
70171568Sscottlstatic char *status_class3[] = {
71171568Sscottl     "Target error",
72171568Sscottl     "Service unavailable",
73171568Sscottl     "Out of resources"
74171568Sscottl};
75171568Sscottl#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
76171568Sscottl
77171568Sscottlstatic char *
78171568SscottlselectFrom(char *str, token_t *list)
79171568Sscottl{
80171568Sscottl     char	*sep, *sp;
81171568Sscottl     token_t	*lp;
82171568Sscottl     int	n;
83171568Sscottl
84171568Sscottl     sp = str;
85171568Sscottl     do {
86171568Sscottl	  sep = strchr(sp, ',');
87171568Sscottl	  if(sep != NULL)
88171568Sscottl	       n = sep - sp;
89171568Sscottl	  else
90171568Sscottl	       n = strlen(sp);
91171568Sscottl
92171568Sscottl	  for(lp = list; lp->name != NULL; lp++) {
93171568Sscottl	       if(strncasecmp(lp->name, sp, n) == 0)
94171568Sscottl		    return strdup(lp->name);
95171568Sscottl	  }
96171568Sscottl	  sp = sep + 1;
97171568Sscottl     } while(sep != NULL);
98171568Sscottl
99171568Sscottl     return NULL;
100171568Sscottl}
101171568Sscottl
102171568Sscottlstatic char *
103171568Sscottlgetkeyval(char *key, pdu_t *pp)
104171568Sscottl{
105171568Sscottl    char	*ptr;
106171568Sscottl    int	klen, len, n;
107171568Sscottl
108171568Sscottl    debug_called(3);
109171568Sscottl
110171568Sscottl    len = pp->ds_len;
111171568Sscottl    ptr = (char *)pp->ds;
112171568Sscottl    klen = strlen(key);
113171568Sscottl    while(len > klen) {
114171568Sscottl	 if(strncmp(key, ptr, klen) == 0)
115171568Sscottl	      return ptr+klen;
116171568Sscottl	 n = strlen(ptr) + 1;
117171568Sscottl	 len -= n;
118171568Sscottl	 ptr += n;
119171568Sscottl    }
120171568Sscottl    return 0;
121171568Sscottl}
122171568Sscottl
123171568Sscottlstatic int
124171568SscottlhandleTgtResp(isess_t *sess, pdu_t *pp)
125171568Sscottl{
126171568Sscottl     isc_opt_t	*op = sess->op;
127171568Sscottl     char	*np, *rp, *d1, *d2;
128171568Sscottl     int	res, l1, l2;
129171568Sscottl
130171568Sscottl     res = -1;
131171568Sscottl     if(((np = getkeyval("CHAP_N=", pp)) == NULL) ||
132171568Sscottl	((rp = getkeyval("CHAP_R=", pp)) == NULL))
133171568Sscottl	  goto out;
134171568Sscottl     if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) {
135171568Sscottl	  fprintf(stderr, "%s does not match\n", np);
136171568Sscottl	  goto out;
137171568Sscottl     }
138171568Sscottl     l1 = str2bin(op->tgtChapDigest, &d1);
139171568Sscottl     l2 = str2bin(rp, &d2);
140171568Sscottl
141171568Sscottl     debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp);
142171568Sscottl     if(l1 == l2 && memcmp(d1, d2, l1) == 0)
143171568Sscottl	res = 0;
144171568Sscottl     if(l1)
145171568Sscottl	  free(d1);
146171568Sscottl     if(l2)
147171568Sscottl	  free(d2);
148171568Sscottl out:
149171568Sscottl     free(op->tgtChapDigest);
150171568Sscottl     op->tgtChapDigest = NULL;
151171568Sscottl
152171568Sscottl     debug(3, "res=%d", res);
153171568Sscottl
154171568Sscottl     return res;
155171568Sscottl}
156171568Sscottl
157171568Sscottlstatic void
158171568SscottlprocessParams(isess_t *sess, pdu_t *pp)
159171568Sscottl{
160171568Sscottl     isc_opt_t		*op = sess->op;
161171568Sscottl     int		len, klen, n;
162171568Sscottl     char		*eq, *ptr;
163171568Sscottl
164171568Sscottl     debug_called(3);
165171568Sscottl
166171568Sscottl     len = pp->ds_len;
167171568Sscottl     ptr = (char *)pp->ds;
168171568Sscottl     while(len > 0) {
169171568Sscottl	  if(vflag > 1)
170171568Sscottl	       printf("got: len=%d %s\n", len, ptr);
171171568Sscottl	  klen = 0;
172171568Sscottl	  if((eq = strchr(ptr, '=')) != NULL)
173171568Sscottl	       klen = eq - ptr;
174171568Sscottl	  if(klen > 0) {
175171568Sscottl	       if(strncmp(ptr, "TargetAddress", klen) == 0) {
176171568Sscottl		    char	*p, *q;
177171568Sscottl
178171568Sscottl		    // TargetAddress=domainname[:port][,portal-group-tag]
179171568Sscottl		    // XXX: if(op->targetAddress) free(op->targetAddress);
180171568Sscottl		    q = op->targetAddress = strdup(eq+1);
181171568Sscottl		    if(*q == '[') {
182171568Sscottl			 // bracketed IPv6
183171568Sscottl			 if((q = strchr(q, ']')) != NULL)
184171568Sscottl			      q++;
185171568Sscottl			 else
186171568Sscottl			      q = op->targetAddress;
187171568Sscottl		    }
188171568Sscottl		    if((p = strchr(q, ',')) != NULL) {
189171568Sscottl			 *p++ = 0;
190171568Sscottl			 op->targetPortalGroupTag = atoi(p);
191171568Sscottl		    }
192171568Sscottl		    if((p = strchr(q, ':')) != NULL) {
193171568Sscottl			 *p++ = 0;
194171568Sscottl			 op->port = atoi(p);
195171568Sscottl		    }
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) {
233171568Sscottl	  int	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